I repeat this function 2500 times in a loop for different parameters and it takes 85 seconds. What is wrong with this function? How can I improve running time? Thanks for your help.
Function:
int findBaconNumber(v * actors[], int actorCount, int index, v * visited[]){
// Bacon number is 0:
if(strcmp(actors[index]->name, "Bacon,Kevin") == 0)
return 0;
// Bacon number is infinite:
else if(actors[index]->next == NULL)
return -1;
// Otherwise, calculate:
memset(visited, NULL, sizeof(visited));
q * queue = createQueue();
v * tmp = actors[index];
v * found;
n * tmp2;
int baconNumber = 0;
int visitCount = 0;
int empty = 0;
int full = 0;
// Add first item to queue:
enqueue(queue, tmp);
visited[visitCount] = tmp;
visitCount++;
tmp->parent = NULL;
// Until queue is empty, queue is full or kevin bacon found:
while(strcmp(tmp->name, "Bacon, Kevin") != 0 && isEmpty(queue) != 1 && isFull(queue) != 1){
// Get neighbors:
if(tmp->next != NULL)
tmp2 = tmp->next;
else
tmp2 = NULL;
// Add neighbors to queue:
while(tmp2 != NULL){
if(contains(visited,visitCount+1,actors[tmp2->actorNo]) != 1){
enqueue(queue, actors[tmp2->actorNo]);
visited[visitCount] = actors[tmp2->actorNo];
visitCount++;
actors[tmp2->actorNo]->parent = tmp;
}
tmp2 = tmp2->next;
}
// Dequeue current item and skip to next item:
dequeue(queue);
// Get next item in queue:
if(getFront(queue) != NULL)
tmp = getFront(queue);
}
if(strcmp(tmp->name, "Bacon, Kevin") != 0)
return -1;
while(tmp->parent != NULL && tmp != NULL){
baconNumber++;
tmp = tmp->parent;
}
return baconNumber;
}
For Loop:
for(i=0; i<actorCount; i++){
baconNumbers[i] = findBaconNumber(actors, actorCount, i, visited);
}
EDIT:
Thanks all of you for answers. This is my school project and it must be solved by breadth first search algorithm so I can not use other solutions. I profiled the code it looks like the problem is with the contains function. I use it to check if node is visited or not. Now, I am working on finding another solution for this.
img1
img2
Some things that gets to messy to write in comments:
1) Use a profiler to find out what parts of the code that is actually taking time. It can be wise to break out all loops into functions to get more useful information from the profiler.
2) Use the information from the profiler to optimize the code
You're not showing the implementation of queue. That could be the bottleneck. If it is, there are basically two ways to attack it. Either try to optimize the queue code, or change the overall algorithm to use the queue less or in a smarter way.
Solving the Bacon number is basically a shortest path problem, so you might want to tinker with different algorithms. Dijkstras and A* are common shortest path algorithms.
Another thing you might look into is preprocessing the data. Right now, you have a struct array, and the structs have a field called name. Maybe you could change this to an int where 1 indicates that the name is "Bacon,Kevin" and 0 that it's not. That would save you potentially costly calls to strcmp and might also make it more cache friendly.
You might want to change the representation completely. Right now, I get the impression that you have something like this:
struct actor {
struct actor *parent, *next;
char *name;
}
I already mentioned that you could change name to a simple integer, but perhaps you could also change everything to:
int *names;
int *parents;
int *next;
That could make the code a lot more cache friendly. Or not. But it is worth trying. Linked lists are usually not very cache friendly. And this can be good to think about when it comes to the queue too.
Also, you might have a look at this: Calculating "Kevin Bacon" Numbers
I solved this problem with your helpful advices.
The way I used before (Too slow => takes 85 seconds for my input):
-When a node is visited, append this node to the end of visited[] array.
-Checking if node is visited or not by looping over all elements of visited[] array (via contains() function)
Faster way which I am using now (1.75 seconds for the same input):
-When a node is visited, assign this node to related index => (visited[actorNumber] = node;)
-Checking if node is visited or not without looping over all elements, only checking the related index.
I also removed strcmp and I am checking by index now.
Updated code is here:
int findBaconNumber(v * actors[], int actorCount, int index, int baconsIndex){
// Bacon number is 0:
if(actors[index]->index == baconsIndex)
return 0;
// Bacon number is infinite:
else if(actors[index]->next == NULL)
return -1;
// Otherwise, calculate:
q * queue = createQueue();
v * visited[ACTORS_SIZE] = {};
v * tmp = actors[index];
v * found;
n * tmp2;
int baconNumber = 0;
int empty = 0;
int full = 0;
// Add first item to queue:
enqueue(queue, tmp);
visited[tmp->index] = tmp;
tmp->parent = NULL;
// Until queue is empty, queue is full or kevin bacon found:
while(tmp->index != baconsIndex && isEmpty(queue) != 1 && isFull(queue) != 1){
// Get neighbors:
if(tmp->next != NULL)
tmp2 = tmp->next;
else
tmp2 = NULL;
// Add neighbors to queue:
while(tmp2 != NULL){
// If not visited:
if(visited[tmp2->actorNo] == NULL){
enqueue(queue, actors[tmp2->actorNo]);
visited[tmp2->actorNo] = actors[tmp2->actorNo];
actors[tmp2->actorNo]->parent = tmp;
}
tmp2 = tmp2->next;
}
// Dequeue current item and skip to next item:
dequeue(queue);
// Get next item in queue:
if(getFront(queue) != NULL)
tmp = getFront(queue);
}
if(tmp->index != baconsIndex)
return -1;
while(tmp->parent != NULL && tmp != NULL){
baconNumber++;
tmp = tmp->parent;
}
return baconNumber;
}
Related
My original code to display nodes in a queue in order from lowest to greatest:
void display (queue *q) {
node *ptr = q->front;
int i = 0;
int size = q->size;
while (i <= size) {
while (ptr->id != i) {
ptr = ptr->next;
}
if (i == 0) {
printf("%d ", ptr->id);
printf("%d\n", ptr->running);
}
else {
printf("%d ", ptr->id);
}
i++;
ptr = q->front;
}
}
Had kept producing Segmentation Fault (Core Dumped) errors. I have malloc the two variables being compared and this error has been fixed.
void display (queue *q) {
node *ptr = malloc(10);
ptr = q->front;
int *i = NULL;
i = malloc(sizeof(int));
*i = 0;
int size = q->size;
while(*i <= size){
while (ptr->id != *i) {
ptr = ptr->next;
}
if (*i == 0) {
printf("%d %d\n", ptr->id, ptr->running);
}
else {
printf("%d %d %d %d\n", ptr->id, ptr->running, ptr->ready, ptr->blocked);
}
i = i + 1 * (sizeof(char));
ptr = q->front;
}
}
However now this doesn't produce the output that I want. I want to increment the i pointer so that it can be the same as my original code.
This has been immensely frustrating, any help would be greatly appreciated!
If I read your first code listing correctly, there's at least one important thing here you need to think about. You seem to have a linked list here, and you're iterating over that list using ptr = ptr->next. This means you need to know when to stop. One common way of setting up a linked list is that the last item in the list has a next value of NULL. Then process the nodes one at a time, and once you are done with one node, you you check whether the next value is NULL: if it isn't you can move on that that next node, if it is NULL you stop.
Here you're not doing checks like this, so you need another way to ensure that you know when to stop. What you seem to be doing is taking the value of q->size and using that to inform you how many items there are in the linked list. So the first thing to think about is how confident you are that that value is correct. For example, if the code building the list puts only two items into the list, bet sets size to three, you'll end up falling off the end of the list, and a segmentation fault is not unlikely.
But there's something even more important than that. Even if you're getting the correct number of items in the list from q->size, you're comparing your loop variable i to size like this:
int i = 0;
while (i <= size) {
⋮
}
This is going to loop with i having the values [ 0, 1, … size ], which is actually size + 1 times. If you want to loop exactly size times, you want a test like i < size rather than i <= size. This is a common mistake, often called an off-by-one error.
Unfortunately, your second listing complicates things, rather than making them better. Go back to your first one and see if you can fix the things I've mentioned here.
EDIT: So, it turns out that 'index' was not being returned to 0. Well then. That fixed one segfault. But still getting a different segfault. Working on it.
node* new_node(void){
node* ptr = malloc(sizeof(node));
for (int i = 0; i<27; i++) {
ptr->next[i] = NULL;
}
return ptr;
}
bool load(const char* dictionary)
{
FILE* dict = fopen(dictionary, "r");
node* ptr = new_node;
char word[LENGTH+1];
int index = 0;
for (int c = fgetc(dict); c!=EOF; c = fgetc(dict)){
if(c!='\n'){
word[index]=c;
index++;
}
else {
for(int x=0; x<=index; x++){
int ch = (word[x] == '\'') ? 26 : tolower(word[x])-'a';
if (ptr->next[ch] == NULL){
ptr->next[ch] = new_node;
}
ptr = ptr->next[ch];
}
ptr->end=true;
}
}
return true;
}
I'm trying to implement a trie data structure for a dictionary but my program seems to segfault somewhere in this function. I can't seem to pin it down even with the help of GDB, so can someone give me a hand?
Node is defined as such:
typedef struct node{
bool end;
struct node* next[27];
} node;
Dictionary file:
a
aaa
aaas
aachen
aalborg
aalesund
aardvark
aardvark's
aardvarks
aardwolf
(...)
You have many issues in your code:
When you allocate memory with malloc, it is uninitialised. initialise it directly after allocating it, so that NULL pointers really are null. (calloc, a cousin of ´malloc´, initialises all memory to zero.)
When you loop over the word, you should nor include index:
for (int x = 0; x < index; x++) ...
When you have found the end of a word, you must reset the index to 0. Otherwise, you will append to the old word and overflow the buffer. (You should probably also enforce the upper bound of ´index´.)
Likewise, when you insert a word into the trie, you must reset your pointer for trie traversal to the trie's root. You need two pointers here: A root node pointer and an auxiliary pointer for traversing the trie.
As is, your trie is local to your function. Return the root node, so that other functions can use the trie, or NULL on failure.
Fix these, and you will have a non-crashing function. (It still leaks memory and may not construct the trie properly.)
node *load(const char *dictionary)
{
FILE *dict = fopen(dictionary, "r");
node *head = calloc(1, sizeof(node));
char word[LENGTH + 1];
int index = 0;
for (int c = fgetc(dict); c != EOF; c = fgetc(dict)) {
if (c != '\n') {
word[index] = c;
index++;
} else {
node *ptr = head;
for (int x = 0; x < index; x++) {
int ch = (word[x] == '\'') ? 26 : tolower(word[x]) - 'a';
if (ptr->next[ch] == NULL) {
ptr->next[ch] = calloc(1, sizeof(node));
}
ptr = ptr->next[ch];
}
ptr->end = true;
index = 0;
}
}
return head;
}
The line:
node* ptr = new_node;
and
ptr->next[ch] = new_node;
are not calling the function, but assigning the address of the function to ptr. Call the function instead.
This problem could have been prevented if compiler warnings: -Wall and -Wextra were enabled.
There is no bounds checking done on the array word. Use the value LENGTH to check if the index is in bounds before using it.
It isn't clear what the if statement inside the for loop is doing. It appears that every time a newline is found the whole array word is added to the tree, but the index isn't reset so the same array is added multiple times. At some point index will point out of bounds causing undefined behavior. You should reset index after you use the array word.
You forgot to reset index to 0 at the beginning of the loop.
You should also use calloc(1, sizeof(node)) instead of malloc(sizeof(node)) to avoid leaving memory uninitialized. I suggest you use valgrind to help you track problems of this kind in your code.
You should filter punctuation\unsupported characters a bit more. Any character outside of [a-z|A-Z|\n|\\] will crash your program because of
int ch = (word[x] == '\'') ? 26 : tolower(word[x])-'a';
if (ptr->next[ch] == NULL){
Given that you open a file, there might be a space somewhere or some unexpected character. You need something like
if(c!='\n'){
int num = (c == '\'') ? 26 : tolower(c)-'a');
if(num >=0 && num < 27)
{
word[index]=c;
index++;
}
}
I am working on a linked list which I have basically working but I need to extend what I currently have but I am having a problem.
I have a structure which stores outbound call legs from phone calls. I am storing these calls in a linked list which is defined as below:
typedef struct CallLogSearchOutboundStruct
{
char * target;
float duration;
char * cleardownCause;
BOOL allowOverwrite;
struct CallLogSearchOutboundStruct * nextLeg;
} callLogSearchOutboundStruct;
I have the basic code working that I can successfully add a new outbound call onto the end of the linked list using the code below:
void insertOutboundLegToList(callLogSearchOutboundStruct * outboundLeg, char * target, float duration, int cleardown, BOOL overwriteFirstOutboundLegs)
{
f (outboundLeg->target == NULL)
{
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
outboundLeg->allowOverwrite = FALSE;
outboundLeg->nextLeg = NULL;
}
else
{
if (overwriteFirstOutboundLegs == FALSE)
{
while (outboundLeg->nextLeg != NULL)
{
outboundLeg = outboundLeg->nextLeg;
}
}
if (outboundLeg->nextLeg == NULL)
{
outboundLeg->nextLeg = (callLogSearchOutboundStruct*)malloc(sizeof(callLogSearchOutboundStruct));
outboundLeg = outboundLeg->nextLeg;
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
outboundLeg->nextLeg = NULL;
}
else
{
outboundLeg->target = NULL;
outboundLeg->duration = 0;
outboundLeg->cleardownCause = NULL;
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
}
}
}
This code is working however I need to modify it so that if the allowOverwrite flag is set it will then first the first outbound leg in the linked list, overwrite it and set the overwrite for the first leg to false, but all other legs in the list are set to allow overwrite.
Therefore, when a new outbound call needs to be inserted, if the overwrite first leg is set to false, then the program will need to loop through each outbound leg and check if the allow overwrite for that leg is set to true and if so overwrite that leg and then set the overwrite flag to false, then again on the next outbound leg, keep looping through until it sees allow overwrite true, overwrite and set to false, this should continue until the next leg is NULL then it just inserts the outbound leg onto the end as normal.
I think I have the base logic correct however, I seem to keep on NULL'ing the first leg, when I break from the loop so I end up with no outbound legs.
Below is how I have modified the code to try and achieve what I need.
if (overwriteFirstOutboundLegs == TRUE)
{
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
outboundLeg->allowOverwrite = FALSE;
//Loop through existing outbound legs and set overwrite flag to TRUE
while (outboundLeg->nextLeg != NULL)
{
outboundLeg = outboundLeg->nextLeg;
outboundLeg->allowOverwrite = TRUE;
}
outboundLeg->nextLeg = NULL;
}
else
{
if (outboundLeg->target == NULL)
{
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
outboundLeg->allowOverwrite = FALSE;
outboundLeg->nextLeg = NULL;
}
else
{
if (outboundLeg->nextLeg == NULL)
{
outboundLeg->nextLeg = (callLogSearchOutboundStruct*)malloc(sizeof(callLogSearchOutboundStruct));
outboundLeg = outboundLeg->nextLeg;
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
outboundLeg->allowOverwrite = FALSE;
outboundLeg->nextLeg = NULL;
}
else
{
while (outboundLeg->nextLeg != NULL)
{
outboundLeg = outboundLeg->nextLeg;
if (outboundLeg->allowOverwrite == TRUE)
{
break;
}
}
outboundLeg->target = strdup(target);
outboundLeg->duration = duration;
outboundLeg->cleardownCause = strdup(setCallResult(cleardown));
outboundLeg->allowOverwrite = FALSE;
outboundLeg->nextLeg = NULL;
}
}
}
I am calling the function using the following code:
insertOutboundLegToList(outboundCallLegStartPtr, targetBuffer, durationBuffer, atoi(rowReport[cleardownColIndex]), overwriteFirstOutboundLegs);
Attached below is also a diagram showing the flow that I need for inserting a new leg.
Thanks for any help you can provide.
I find it useful to explore the actual problem with a small program dedicated to the exact problem at hand.
First, I think there is an error or omission in the diagram. I do believe you wish to always clear the overwrite flag for the inserted or replaced leg. So, that's what the following example program does:
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
typedef struct node {
struct node *next;
int overwrite;
int value;
} node;
node *insert(node **const listptr, int value, int overwrite)
{
/* No list specified? */
if (!listptr) {
errno = EINVAL;
return NULL;
}
/* Empty list? */
if (!*listptr) {
node *newnode;
newnode = malloc(sizeof *newnode);
if (!newnode) {
errno = ENOMEM;
return NULL;
}
newnode->next = NULL;
newnode->value = value;
newnode->overwrite = 0;
*listptr = newnode;
return newnode;
}
if (overwrite) {
node *const currnode = *listptr;
node *temp;
/* Overwrite contents */
currnode->value = value;
currnode->overwrite = 0;
/* Set overwrite flag for all nodes that follow */
temp = currnode->next;
while (temp) {
temp->overwrite = 1;
temp = temp->next;
}
return currnode;
} else {
node **ptr = listptr;
node *currnode = *listptr; /* always equal to *ptr */
/* Find the first overwritable node */
while (currnode && !currnode->overwrite) {
ptr = &currnode->next;
currnode = currnode->next;
}
/* Found an overwritable node? */
if (currnode) {
currnode->value = value;
currnode->overwrite = 0;
return currnode;
}
/* Construct a new node to be appended to the list. */
currnode = malloc(sizeof *currnode);
if (!currnode) {
errno = ENOMEM;
return NULL;
}
currnode->next = NULL;
currnode->value = value;
currnode->overwrite = 0;
/* Append to the list. */
*ptr = currnode;
return currnode;
}
}
void display(const char *const header, const node *list, const char *const footer)
{
if (header)
fputs(header, stdout);
if (list) {
do {
if (list->overwrite)
printf("/%d", list->value);
else
printf("%d", list->value);
list = list->next;
if (list)
putchar(' ');
} while (list);
} else
fputs("(empty)", stdout);
if (footer)
fputs(footer, stdout);
}
int main(int argc, char *argv[])
{
node *list = NULL;
int arg, value;
char dummy;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s VALUE ... VALUE\n", argv[0]);
fprintf(stderr, "Where VALUE is\n");
fprintf(stderr, " /INTEGER to insert-overwrite INTEGER, or\n");
fprintf(stderr, " INTEGER to insert INTEGER normally.\n");
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
display("Initial list: ", list, ".\n");
for (arg = 1; arg < argc; arg++) {
if (sscanf(argv[arg], " /%d %c", &value, &dummy) == 1) {
if (!insert(&list, value, 1)) {
fflush(stdout);
fprintf(stderr, "Cannot insert-overwrite %s: %s.\n", argv[arg], strerror(errno));
return EXIT_FAILURE;
} else
printf("Inserted %d with overwrite set:\n", value);
} else
if (sscanf(argv[arg], " %d %c", &value, &dummy) == 1) {
if (!insert(&list, value, 0)) {
fflush(stdout);
fprintf(stderr, "Cannot insert %s: %s.\n", argv[arg], strerror(errno));
return EXIT_FAILURE;
} else
printf("Inserted %d:\n", value);
} else {
fflush(stdout);
fprintf(stderr, "%s: Not a valid VALUE.\n", argv[arg]);
return EXIT_FAILURE;
}
display("\t", list, ".\n");
}
display("Final list: ", list, ".\n");
return EXIT_SUCCESS;
}
The idea is that you give a test sequence as command line parameters, each value being an integer (heavily simplifying your leg definitions!). Precede the value with a slash if it should be inserted with overwriting set.
You can compile the above with for example
gcc -W -Wall -O3 example.c -o example
Let's consider the test sequence 1 2 3 /4 5 /6, meaning we insert the six first positive integers in order, but 4 and 6 with overwrite flag set (and unset for all others):
./example 1 2 3 /4 5 /6
which outputs
Initial list: (empty).
Inserted 1:
1.
Inserted 2:
1 2.
Inserted 3:
1 2 3.
Inserted 4 with overwrite set:
4 /2 /3.
Inserted 5:
4 5 /3.
Inserted 6 with overwrite set:
6 /5 /3.
Final list: 6 /5 /3.
After the three legs are inserted, the path is obviously just 1 2 3, since the overwrite flag was not set for any of them, and initially the list was empty.
When 4 is inserted with overwrite set, my logic overwrites the first one, clears the overwrite flag for it (in contradiction to the logic description in the question), and sets the overwrite flag for the rest of the legs in the path. Therefore, the path becomes 4 /2 /3.
When 5 is inserted, it replaces 2, since 2 had overwrite flag set. Again, in contradiction to the logic description in the question, I clear the overwrite flag for 5. Therefore, the path becomes 4 5 /3.
When 6 is inserted with overwrite set, it overwrites the first one. Again, I clear the overwrite flag for it, in contradiction to the logic described in the question, but set for all the rest of the legs in the path, so the path becomes 6 /5 /3.
First, a minor note about the node structure: The fact that I put the next pointer at the start of the node structure is just a habit.
(It may help the compiler generate better code on some architectures, because the next pointer then points to the address where the next->next is, and that may help the compiler or the processor do simpler instructions and better prefetching patterns when doing a path walk. If you put the next pointer somewhere else, the next->next is at a fixed offset of that address; in some cases that may require an extra instruction. Does it matter in practice? Usually not, not even a single CPU cycle.)
The insert() function implementation should be quite straightforward:
If the list is empty, create a new node, setting its overwrite flag to false. Done.
Otherwise:
If overwriting is desired, replace the first node, setting its overwrite flag to false, and the overwrite flag for all other nodes to true. Done.
Otherwise:
If there is a node with overwrite flag true, replace that node, resetting its overwrite flag to false. Done.
Otherwise:
Create a new node, setting its overwrite flag to false. Append the new node to the end of the list. Done.
The only "trick" you might wonder about, is how the ptr pointer is used. Simply put, it is the address where the currnode pointer was obtained from, essentially currnode = *ptr. This way, instead of checking for currnode->next within the loop, we just traverse the list until currnode becomes NULL. Then, *ptr refers to the ->next pointer in the final element in the list, and we can just assign to it, to append the newly created node to the list.
I realize that this does not answer the OP's question, but that is because I don't know how to tackle the two levels of issues in the original question at the same time -- I believe there is both that logic issue (with the overwrite flags), and some kind of an implementation problem related to how the linked list is managed.
When debugging, fixing programs, or writing new programs, I always try to limit the possible sources of problems to the smallest possible set. Writing limited, simplified test cases like the above program lets me concentrate on one thing at a time, without having to switch my brain between the overall logic and the nitty-gritty details of implementation. Simply put, this is the way I'd work through the OP's problem, if it was my own code.
Now that I have an example code that implements the logic I believe is correct, and I can easily stress-test the logic, I can implement the more complex actual leg structure and leg insertion function. Knowing the logic works (and I can verify any corner case using the test program if I get any doubts), I can concentrate on the actual implementation.
Obviously, it is up to the OP now to decide which logic (their or mine) to use, and if using mine, to see how our implementations differ; I don't think the OP posted enough code (the full insertion routine) to tell where the actual problems are. At least I cannot get a full enough overview to be sure.
Anyway, hope this helps. Questions?
One problem (perhaps there are others) appears that you are setting outboundLeg->nextLeg = NULL; before you attempt to iterate through the linked list. So you're terminating it and therefore will never be able to set the rest to allow overwrite. Looks like a copy-paste bug.
Edit: Another potential problem is that if overwriteFirstOutboundLegs == FALSE and the passed-in outboundLeg->nextLeg != NULL and no outboundLeg->allowOverwrite == TRUE is encountered in the list then the last list item will be overwritten (even though it is not flagged to allow overwrite) instead of allocating a new struct to append to the end of the list.
I am currently using a list in structs that looks like this:
This is a function in which removes elements from a list. I start with a for loop to go through the entire list. If i is less than the number of entries it enters an if statement. Then it the old position into a hold ptr. Makes the old on = to NULL and then moves the list so that the elements below take its spot.
Heres a sample list when I call this function:
100
125
150
When I do this and I want to remove 150 from the list it goes through but looses access to memory in the list -> wlist_ptr[i] -> eth_address. I then get a set fault. Is there any way around loosing track?
There's a big mistake in the loop.
Only one element is "moved up", and it's moved up after it's set to null.
So, this
list -> wlist_ptr[i] = NULL;
list->wlist_ptr[i-1] = list->wlist_ptr[i];
Needs to change to this to prevent moving the NULL up:
list->wlist_ptr[i-1] = list->wlist_ptr[i];
list -> wlist_ptr[i] = NULL;
But then, a loop is needed to iterate through the remainder of the list to move them up as well. memmove is your friend for this. Keep in mind too, that when that's done, you don't want to increase i for the next iteration, because the next element in the list will now be at the original i location.
Perhaps this will do the job:
struct wifi_info_t *wifilist_remove(struct wifilist_t * list, int user_address)
{
int i;
int count;
struct wifi_info_t *ptr;
ptr = NULL;
count = wifilist_number_entries(list);
// TODO: take out the ( ptr == NULL ) logic if more than one match needs to be
// removed.
for(i=0; ( i < count ) && ( ptr == NULL ); i++)
{
if(list -> wlist_ptr[i] -> eth_address == user_address)
{
ptr = list -> wlist_ptr[i];
if ( i < ( count - 1 ) )
memmove(&(wlist_ptr[i]), &(wlist_ptr[i + 1]), (count - (i + 1)) * sizeof(wlist_ptr[0]));
// TODO: decrement the length of the list returned by
// wifilist_number_entries(list)
}
}
if(ptr != NULL)
{
list->wlist_entries--;
}
return ptr;
}
Note that I just typed this here, so it may have syntax errors or the like.
Some example:
struct wifi_info_t *wifilist_remove(struct wifilist_t * list, int user_address)
{
int i;
struct wifi_info_t *ptr;
ptr = NULL;
for(i=0; i < wifilist_number_entries(list); i++)
{
if(list -> wlist_ptr[i] -> eth_address == user_address)
{
ptr = list -> wlist_ptr[i];
if(i != (wifilist_number_entries(list) -1))
{
//replace it with last element
list -> wlist_ptr[i] = list -> wlist_ptr[wifilist_number_entries(list)-1];
list -> wlist_ptr[wifilist_number_entries(list)-1] = ptr;
}
//you can use free and realloc there if you want
list->wlist_entries--;
}
}
//why? don't do that
return ptr;
}
For this assignment I had to create my own string class. I initially wrote the compareto method to compare two string but return whichever is overall larger. What I want to do is compare and return which one is alphabetically larger i.e. comparing two strings, for example: smith and htims. With the way I designed the compareto method is that the result will be that they are equal. What I want to do is tell me which one comes first alphabetically, so for my example htims would come first. I understand how to do this in Java or even in C with using the <string.h> library, I am just confused as to how to do this myself.
EDIT: I just wanted to note that I am not looking for code answer, rather a nudge in the how I should write the code.
int compareto(void * S1, void * S2){
String s1 = (String S1);
String s2 = (String S2);
int i, cs1 = 0, cs2 = 0; //cs1 is count of s1, cs2 is count of s2
while(s1->c[i] != '\0'){ //basically, while there is a word
if(s1->c[i] < s2->c[i]) // if string 1 char is less than string 2 char
cs2++; //add to string 2 count
else (s1->c[i] > s2->c[i]) //vice versa
cs1++;
i++;
}
//for my return I basically have
if(cs1>cs2){
return 1;
}
else if(cs2 > cs1){
return 2;
}
return 0;
here is mystring.h
typedef struct mystring {
char * c;
int length;
int (*sLength)(void * s);
char (*charAt)(void * s, int i);
int (*compareTo)(void * s1, void * s2);
struct mystring * (*concat)(void * s1, void * s2);
struct mystring * (*subString)(void * s, int begin, int end);
void (*printS)(void * s);
} string_t;
typedef string_t * String;
Any suggestions, all of my google searches involve using the <string.h> library, so I've had no luck.
Im using this to traverse through a linked list and remove the person whose last name matches the person the user is trying to delete.
Here is my test code to help clarify my problem (Note that compareto is in the remove function):
int main() {
Node startnode, currentnode, newnode;
int ans, success;
String who;
who = newString2();
startnode = (Node) malloc(sizeof(pq_t));
startnode->next = NULL;
currentnode = startnode;
ans = menu();
while (ans != 0) {
switch (ans) {
case add:
newnode = getStudent();
startnode = insert(newnode, startnode);
break;
case remove:
printf("Enter the last name of the person you want to delete : \n");
scanf("%s", &who->c);
startnode = removeStudent(startnode, who, &success);
if (success == 0)
printf("UNFOUND\n");
else
printf("permanently DELETED\n");
break;
case view:
printf("Now displaying the list : \n");
displaylist(startnode);
break;
}
ans = menu();
}
}
Node removeStudent(Node head, String who, int * success) {
Node p, l; //p = pointer node, l = previous node
Student current; //Im using generics, so I have to case the current node->obj as a student.
String ln, cln; //the last name of the person I want to remove, and the last name of the current node
p = head;
l = p;
//there can be three cases, p->first node, p->last node, p->some node in between
if (head->obj == NULL) {
printf("The list is empty\n"); //when the list is empty
*success = 0;
return NULL;
}
while (p != NULL) {
current = (Student) p->obj;
cln = current->ln;
if (ln->compareTo(who, cln) == 0) {
if (head == p) { //when there is only one node
head = head->next;
free(p);
*success = 1;
return head;
} else if (p->next == NULL) { //last node
l->next = NULL;
free(p);
*success = 1;
return head;
} else {
l->next = p->next; //middle
free(p);
*success = 1;
return head;
}
}
l = p;
p = p->next;
}
*success = 0;
return head;//couldnt find the node
}
Try comparing the following pairs of strings:
"ABC" vs "DEF"
"ADF" vs "BBB"
"ABC" vs "CBA"
What results do you get? More importantly, why? How do these results compare to what you want to get?
(You should first work it out in your head. Work out the values of c1 and c2 for each step of the comparison loop.)
First, ln isn't properly initialized in the sample removeStudent(), so calling ln->compareTo will probably cause a segfault. Hopefully, ln is properly initialized in your actual code.
To define an ordering on the strings, you can first define what's known in database circles as a "collation": an ordering on characters. You can implement the collation as a function (called, say, chrcmp), or inline within your string comparison function. The important thing is to define it.
Generally speaking, an ordering on a type induces a lexicographic order on sequences of that type: to compare two sequences, find the first place they differ; the lesser sequence is the one with the lesser element at that position.
More formally, suppose sequences are indexed starting at 0. let a and b be sequences of the base type of lengths m and n, respectively. The lexicographic order a ≤ b is:
a < b where ai R bi and aj=mj for all 0 ≤ j < i
a < b if a is a prefix of b
a=b if m=n and ai=bi for all 0 ≤ i < m
Where "a is a prefix of b" means m < n and ai = bi for all 0 ≤ i < m.
The advantage of this approach is you can write a comparison function that will work with any homogeneous sequence type: strings, lists of strings, arrays of integers, what-have-you. If you specialize the comparison function for null-terminated strings, you don't need to worry about the cases for prefixes; simply have '\0' be the least character in the collation.
From a general comparison function (called, say, lexiCompare), you can define
lexicCompareString (a, b):
return lexicCompare(a, b, chrcmp)
and set a String's compareTo member to lexicCompareString.