Let "List" be a struct that represents a singly-linked list, i have the following function:
int List_contains(List node, const char* key) {
List *search;
search=node.next;
assert(search->key!=NULL);
return 1;
}
and List is the following struct:
typedef struct List { /*A new node*/
char* key;
void* value;
struct List *next;
} List;
The function "List_contains" should tell me if "key" is contained in the list or not. Problem is, i can't iterate through the list, and the line
assert(search->key != NULL);
throws a Segfault. How can i iterate through the list with what i have?
(Note: The function is, obviously, not "completed".)
Here is an example of a search I wrote a while back.
struct node
{
int data;
struct node *next;
};
int
search_list(struct node *head, int search)
{
if (!head)
{
fprintf(stderr, "Invalid head\n");
return 1;
}
struct node *temp = head;
while (temp)
{
if (temp->data == search)
{
printf("Found %d\n", search);
break;
}
temp = temp->next;
}
return 0;
}
int List_contains(List node, const char* key) {
List *search;
search=node.next;
assert(search->key!=NULL);
return 1;
}
so a lot to parse here...
Generally you would pass in a pointer to the node so that it isn't copied, but it is actually safe to do what you have done, in this instance, assuming no multithreaded shenanigans go on...
but in the future I would look for:
int List_contains(List* node, const char* key) {
next line you make a pointer without a value... which is fine, because you assign it the next line, but that could be condensed...
// List *search;
// search=node.next; # could easily become:
List *search = node.next;
now at this point you actually know if you are at the end of the list...
if(search == null)
{
return 0;
}
after that you need to do some sort of compare...
if (strcmp(key,search->key) == 0)
{
//found it;
return 1;
}
now you have code that will match the key against the first element of the list, so you would need to put the whole thing in a loop, each iteration swapping search with search->next and checking for null
Related
I'm currently doing an assignment for uni and I need to find the sum of a graph.
To do this I believe I need a linked list that I can use to remember which nodes have been visited. I have the linkedlist working correctly but I can't get a contains function to work. This is the code I have:
struct listnode
{
struct N *val;
struct listnode *next;
};
int contains(struct listnode *head,struct N* value)
{
struct listnode *current = head;
while (current)
{
if ((current -> val) == value)
{
return 1;
}
current = current -> next;
}
return 0;
}
note: N is a node of the graph.
Can anyone see any problems with what I'm doing?
EDIT: contains function should return 1 when N *value is in the list, 0 otherwise
EDIT2:
I have a push function:
void push(struct listnode *head,struct N *value)
{
if (head)
{
struct listnode *current = head;
while (current->next)
{
current = current -> next;
}
current->next = malloc(sizeof(struct listnode*));
current->next->val = value;
current->next->next = NULL;
}
else
{
head = malloc(sizeof(struct listnode*));
if (head)
{
head -> val = value;
head -> next = NULL;
}
else
{
printf("error");
exit(0);
}
}
}
and I want the following line to return 1:
contains(push(visited,p),p);
where p is a pointer to a struct N and visited is my global linked list
EDIT3:
this is my final sum function that I believe should work, but doesnt because of contains.
long sum(struct N *p)
{
if (p)
{
if (contains(visited,p) == 0) //if p hasnt been visited
{
push(visited,p); //make it visited
return (p -> data) + sum(p -> x) + sum(p -> y) + sum(p -> z);
}
else
{
return 0;
}
}
else
{
return 0;
}
}
Your contains function appears to be fine. The issue is that you are always passing a NULL list to it, which is caused by a faulty push function. You need a return in push, or to pass in a pointer with one more level of indirection, so you can assign to head outside of push. One more possible improvement is to notice that no matter what you pass in, the malloc and initialization of a new node is actually the same.
Finally, the main issue, that is really the most likely to cause a segfault is the fact that you are allocating enough space for a pointer to a node, not for the node itself.
Here is an example:
#ifdef BY_INDIRECTION
#define RET_TYPE void
#define IN_TYPE struct listnode **
#else
#define RET_TYPE struct listnode *
#define IN_TYPE struct listnode *
#endif
RET_TYPE push(IN_TYPE head, struct N *value)
{
struct listnode *current, **next;
if(head)
{
for(current = head; current->next; current = current->next) ;
next = &(current->next);
}
else
{
#ifdef BY_INDIRECTION
next = head;
#else
next = &head;
#endif
}
*next = malloc(sizeof(struct listnode));
if(!*next) {
printf("error");
exit(0);
}
(*next)->val = value;
(*next)->next = NULL;
#ifndef BY_INDIRECTION
return head
#endif
}
I have included both suggestions here. If you want to read the one where we use indirection (pass in a listnode ** and have void return), choose the path where BY_INDIRECTION is defined. If you want to have head returned (and pass in just a regular listnode *) read the path where BY_INDIRECTION is not defined.
The latter approach has a return value, so it can be used to write a shortened form like if(contains(push(head, value), value)) { ... }. The former approach does not, so you would have to do
push(&head, value);
if(contains(head, value)) { ... }
I would recommend using the indirect approach regardless because there are very few instances that you would want to check for containment after putting in a value.
This comparison:
if ((current -> val) == value)
it's comparing pointers. If you call your contains() function this way...
...
struct N val_to_find;
...
result = contains (list, &val_to_find);
You will never find the value, even if the contents of val_to_find are the same as the contents of any struct whose pointer is stored in the list.
If your intention for contains() is to find nodes that have the same data, and not just the same pointers, I'd suggest you something like this:
if (struct_n_comparing_function (current -> val, value) == EQUAL) ...
Where struct_n_comparing_function should have the following prototype:
int struct_n_comparing_function (struct N *a, struct N *b);
which compares the contents of the two structs pointed by a and b and return EQUAL if all the fields of the struct pointed by a have the same value as the fields of struct pointed by b.
I know how pointers works.
I done similar problem with this way
deleteNode(struct node *head_ref, int key);
which is working and # here http://quiz.geeksforgeeks.org/linked-list-set-3-deleting-node/ they have used
deleteNode(struct node **head_ref, int key);
which also correct but is there reason to do so , will 1st one fails in any condition or is it bad way etc.
struct linked_list *deleteNode(struct linked_list *head, int key )
{
struct linked_list *prevNode,*current,*temp;
if( head==NULL)
return head;
if(head->data==key)
{
if(head->next==NULL)
{ free(head);
return NULL;
}
else
temp=head->next;
free(head);
return temp;
}
prevNode= head;
current=head->next;
printf("\n %d\n",(current->data));
while((current!=NULL) && (current->data!=key))
{ printf("\n here");
prevNode= current;
current=current->next;
}
if(current==NULL){
printf("\n element not present in list !\n");
return head;
}
if(current->next==NULL)
prevNode->next=NULL;
else
prevNode->next=current->next;
free(current);
return head;
}
head=deleteNode(head,key);
If you need to delete the head node, the first function won't work because you can't change the head node. The second function takes the address of the head node so it can be changed if need be.
The deleteNode function in the link contains the following:
struct node* temp = *head_ref, *prev;
// If head node itself holds the key to be deleted
if (temp != NULL && temp->data == key)
{
*head_ref = temp->next; // Changed head
free(temp); // free old head
return;
}
You can see here that it dereferences head_ref to change what it points to.
Let's forget the linked list and just think of updating a variable. There are two, equally valid ways to do it:
// 1. pass back
int update_int1(int val) {
return val + 1;
}
void caller1() {
int var = 1;
var = update_int1(var);
}
// 2. write back
void update_int2(int *val) {
*val += 1;
}
void caller2() {
int var = 1;
update_int2(&var);
}
This is easy to understand, so let's do the same thing with a pointer:
// 1. pass back
char *update_ptr1(char *ptr) {
return ptr + 1;
}
void caller1() {
char *ptr = malloc(10);
ptr = update_ptr1(ptr);
}
// 2. write back
void update_ptr2(char **ptr) {
*ptr += 1;
}
void caller2() {
char *ptr = malloc(10);
update_ptr2(&ptr);
}
It works exactly the same as for int! The key is there's always one more star if you want to write back, not pass back.
Which pattern you choose is up to you. The write-back approach is popular for linked lists.
When you write *b==>access contents of address contained in b.
When you write **c==>Access contents of contents of address contained in c.
So, I am reading from a file and inserting national park names into nodesin a double linked list. Now, I want to insert alphabetically, and I thought I was doing that in the insert function, but when I go to print out the list, it is just in the order in which they are inserted in. My guess is that I have an error in the strcmp in the insert function, but I can't figure it out.
I am suppose to use sentinel nodes. So, the first node has "" in it and the last node will have 177 in the data fields. This is my first time using sentinel nodes, and my teacher wanted us to use them, so I apologize if there is errors with them.
Thanks for the help.
#define DUMMY_TRAILER '\177'
typedef struct node NODE;
struct node
{
char data[20]; //for sentinel nodes
char parkName[20];
NODE *forw;
NODE *back;
};
FILE *Open(void); // Opens file
NODE *init_list(void); //Creates the sentinel nodes
void insert(NODE *list, char *name); //Inserts nodes into list
void traverse_forw(NODE *list); // Traverse the list and prints
Main
int _tmain(int argc, _TCHAR* argv[])
{
FILE* inputFile;
NODE *list;
char tempName[31];
inputFile = Open();
list = init_list();
while(fgets(tempName, 31, inputFile) != NULL)
{
insert(list, tempName);
FLUSH;
}
traverse_forw(list);
free(list);
return 0;
}
Insert Function
void insert(NODE *list, char *data)
{
NODE *curr = list->forw;
NODE *prev = list;
NODE *pnew;
int duplicate = 1;
// search
while (strcmp(data,curr->data) > 0)
{
prev = curr;
curr = curr->forw;
}
if (strcmp(data, curr->data))
{
duplicate = 0; // not a duplicate
pnew = (NODE *) malloc(sizeof (NODE));
if (!pnew)
{
printf("Fatal memory allocation error in insert!\n");
exit(3);
}
strcpy(pnew->parkName, data);
pnew->forw = curr;
pnew->back = prev;
prev->forw = pnew;
curr->back = pnew;
}
return;
}
Traverse to print out park names
void traverse_forw(NODE *list)
{
putchar('\n');
list = list->forw; // skip the dummy node
while (list->data[0] != DUMMY_TRAILER)
{
printf("%s\n", list->parkName);
list = list->forw;
}
return;
}
You are storing file read-lines into parkName but comparison is based on the data field
First of all you should create an empty linked list which is equal to NULL, then in the insert function before search, you control if the list is empty or not (in order to not comparing an string with NULL in the first insertion).
another issue is that in this part of the insert function:
while (strcmp(data,curr->data) > 0)
{
prev = curr;
curr = curr->forw;
}
you are only controlling if the data is alphabetically after the current data, I mean you control only forward and if you insert a data which starts with "D" after a data which starts with "R" for instance, your code won't handle that. so you should also consider backward control as well.
Another issue is which pouyan mentioned above, that you are putting data into the data and not in the parkname.
I have the following code which deletes a given node from the linear single linked list.
I want to know if we can still improve this program and does it break anytime
struct node
{
int num;
struct node *next;
} ;
typedef struct node s;
void delete(struct node *first)
{
int flag = 0;
s *ptr, *lastNodePtr = NULL, *deleteNode;
deleteNode = (s*) malloc(sizeof(s));
printf("enter the node value to delete");
scanf_s("%d",&deleteNode->num);
deleteNode->next = NULL;
for (ptr=first;ptr!=NULL;ptr=ptr->next) //at least one element exist
{
if(deleteNode->num == ptr->num)
{
flag=1;
if(ptr==first) //need to delete at first place
{
free(ptr);
first = null; //i dont think we need to do this as it points to ptr and ptr is already null.
}
else // need to delete some where middle.it can be last as well.
{
lastNodePtr->next=ptr->next;
free(ptr);
}
printf("successfully deleted..");
break;
}
lastNodePtr=ptr; // taking note of last node visited..
}
if (flag==0)
{
printf("\n Couldn't find the node");
return;
}
}
if ptr is the first element in the list to delete, you set first to null, not to the next of ptr. (sideeffect: you are not able to free the the rest of the list)
your EDITH: delete should return the new Head, better make it a struct node **first parameter which changes the first element if the first is the deleted one
BTW: never cast the result of malloc.
BTW two. why use for-loop? everybody uses while-loop with linked lists
BTW three: normal variable names for linked lists are "head", "list", "next", "prev", "last" with the nice side-affect, they are all the same length, so making it neatly aligned.
struct node
{
struct node *next;
int num;
} ;
void delete(struct node **pp, int num) {
struct node *del;
for ( ;*pp; pp= &(*pp)->next) {
if((*pp)->num == num) break;
}
if (!*pp) { printf("Couldn't find the node(%d)\n", num); return; }
del = *pp;
*pp = del->next;
free(del);
}
BTW: there is nothing wrong with for() loops; they allow you to put all the loop logic on one line.
hey guys this is my first time doing double linked list so I'm not very sure what I'm doing here ,need some help to check the code,thanks, here is what I have done with comments included.
The functions I have done here are print,print reverse,count elements in the linked list,and search function to determine whether this node exist
void printListfow() //print the list in forward manner
{
CLR;
struct node *tmpval; //declare a temp storage
if(start==NULL) //if 1st node = null,then nth is inside,nth to print
{
printf("List is empty\n");
return;
}
tmpval=start; //assign the head/start to temp storage to retrieve data in 1st node
printf("List of customer details: \n");
while(tmpval!=NULL) //keep doing till it is NULL/the end
{
printf("%d ", tmpval->detail); //print the 'detail' which is in the node temp is pointing at
tmpval=tmpval->next; //assign next node to the temp storage so that it can be printed again
}
}
void printListrev() //print in reverse manner
{
CLR;
struct node *tmpval; //temp storage
if(start==NULL) //
{
printf("List is empty\n");
return;
}
tmpval=start; //assign start to tmpval to retrieve value
printf("List of customer details: \n");
tmpval=tmpval->prev //move backward and assign the data to tmpval
printf("%d",tmpval->detail) //print it
}
void count() //count total number of records
{ struct node *x;
x=start; //assign value of start to temp storage
int ctr=0; //initialize counter
while(x!=NULL)
{
x=x->next; //keep going to next node and then increase the counter
ctr++;
}
printf("Number of customer records are %d\n",ctr);
}
int getNode(node *tmp ,int cust) //when user wants to delete a customer ID and its details, this will search through the list,then if found,pass the value to another function for deletion
{
tmp=tmp->cust;
while(tmp!=NULL)
{
if(tmp->detail == cust) //check if detail[ID stored] is same as requested[cust]
{
return 1;
}tmp=tmp->next; //if not same,then move to next one
}return 0;
}
thanks!
In context to printListrev():
Unless this is a circular doubly linked list, in which case last element is preceded by the first, start would have previous element NULL. So, there is no point in accessing the previous field of start, as you do here:
tmpval=start;
...
tmpval=tmpval->prev;
You can keep another pointer to end of the list for this purpose.
Other alternatives include:
recursive function:
void printrev(struct node *s)
{
if (s == NULL)
return;
printrev(s->next);
printf("%d ", s->detail);
}
iterative function:
void printrev()
{
struct node *end;
for (end = start; end->next != NULL; end = end->next)
;
for (; end != NULL; end = end->prev)
printf("%d ", end->detail);
}
Your getNode is of limited use. Suppose, if you want to delete element, your getnode would only return whether, element is present or not. Say it is present, your deleteNode function would still have to iterate to the appropriate element in the list before deleting it.
This could be solved by getNode returning pointer to the node:
node *getNode(int x)
{
node *t;
for (t = start; t != NULL; t = t->next)
if (t->detail == x)
return t;
return t;
}
Now, you can code delete as follows:
void delNode(node *n)
{
n->prev->next = n->next;
n->next->prev = n->prev;
free(n);
}
And call as follows:
node *n;
if ((n = getNode(x)) != NULL)
delNode(n);
I've assumed that you struct is:
struct node {
int detail;
struct node *next;
struct node *right;
};
typedef struct node * node;
In printListrev() you are printing only one node not going reverse.
One major problem you are doing is in getNode() ,you are changing the address of a local pointer but the original pointer still points where it previously.
If so then how you are going to delete that node ,as you can't know the node address after this function returns.
Are you going to call the getNode() for all the node ,if so that is not a good if you have many nodes.