I was trying to reverse a linked list but I kept comming across an issue. I finally figured out was wrong and fixed it, however I do not understand why my last approach doesn't work. The code below is the one that successfully reverses a linked list.
void reverse_list(list** head)
{
list* currNode = *head;
list* prevNode = NULL;
list* tmpNode;
while(1)
{
tmpNode = currNode->next;
currNode->next = prevNode;
if(tmpNode == NULL)
{
*head = currNode;
break;
}
prevNode = currNode;
currNode = tmpNode;
}
}
However, the code below does not work. Both use double pointers, but in this one I dereferenced head twice to get to the actual object and assigned it with *currNode. When I run this code, It ends up going into an infinite loop and its missing the last item. For example, if the items were 1,2,3,4,5, then the reverse would be 5,4,3,2, and it keeps printing the same list. I don't understand why this approach isn't working since I'm accessing the actual object (by derefrening twice) and assigning it with a new object (*currNode).
void reverse_list(list** head)
{
list* currNode = *head;
list* prevNode = NULL;
list* tmpNode;
while(1)
{
tmpNode = currNode->next;
currNode->next = prevNode;
if(tmpNode == NULL)
{
**head = *currNode;
break;
}
prevNode = currNode;
currNode = tmpNode;
}
}
The code below has the same issue as the one above. I followed the same approach as with the above code only this one uses a single pointer.
void reverse_list(list* head)
{
list* currNode = head;
list* prevNode = NULL;
list* tmpNode;
while(1)
{
tmpNode = currNode->next;
currNode->next = prevNode;
if(tmpNode == NULL)
{
*head = *currNode;
break;
}
prevNode = currNode;
currNode = tmpNode;
}
}
Any help to understand this would greatly be appreciated. Thank you!
Different levels of indirection.
Given list** head, *head is the value pointed to by head, a pointer to a Node, a list *. We dereference one more time and **head dereferences to list * and then to list. We're all out of pointers and and are accessing the object at the end of the line. So
**head = *currNode;
dereferences both pointers all the way back to the objects. This is assigning values to objects, not addresses to pointers to objects. Rather than changing the linkage of the list by updating pointers, whatever list head ultimately pointed at has been changed to match currNode, breaking the integrity of the list.
*head = currNode;
only derefences head one level closer to the object. Here we are operating on pointers and changing linkage.
In the final example we have
*head = *currNode;
and this is similar to the first failed attempt. It is assigning values rather than changing the pointers.
For starters all the three functions in general can invoke undefined behavior when they are called for empty lists due to these lines
list* currNode = head;
//...
tmpNode = currNode->next;
because there is used a null pointer to access memory.
In the second function in this if statement
if(tmpNode == NULL)
{
**head = *currNode;
break;
}
the original first node is overwritten by the last node
**head = *currNode;
So after exiting the function you will have that the original pointer head still points to the originally first node that was overwritten by the last node of the list. So as a result one actual data will be lost and there will be a memory leak.
As for the third function then it is enough to point to that the function accepts the pointer to the head node by value.
void reverse_list(list* head)
That is the function deals with a copy of the value of the original variable head. Changing the copy will not influence on the original pointer. The original pointer will still point to the first node.
The function can look simpler the following way
void reverse( list **head )
{
list *current = *head;
*head = NULL;
while ( current != NULL )
{
struct Node *tmp = current;
current = current->next;
tmp->next = *head;
*head = tmp;
}
}
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
} list;
int push_front( list **head, int data )
{
list *new_node = malloc( sizeof( list ) );
int success = new_node != NULL;
if ( success )
{
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
return success;
}
void display( const list *head )
{
for ( ; head != NULL; head = head->next )
{
printf( "%d -> ", head->data );
}
puts( "null" );
}
void reverse( list **head )
{
list *current = *head;
*head = NULL;
while ( current != NULL )
{
list *tmp = current;
current = current->next;
tmp->next = *head;
*head = tmp;
}
}
int main(void)
{
const int N = 10;
list *head = NULL;
display( head );
reverse( &head );
display( head );
putchar( '\n' );
for ( int i = 0; i < N; i++ )
{
push_front( &head, i );
}
display( head );
reverse( &head );
display( head );
return 0;
}
The program output is
null
null
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
Related
I am trying to write a recursive function to reverse a doubly linked list. This code is not complete yet but I am hit with a issue. The application is not executing completely.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef struct nodes
{
uint8_t x;
struct nodes *next;
struct nodes *prev;
}Node;
Node *head = NULL;
void rev_rec_dll(Node **a, Node **b)
{
//check if it is last node in the list
if((*a)->next != NULL)
{
//set next to prev and prev to next
(*a)->next = (*a)->prev;
(*a)->prev = (*b);
printf("done for node %d and moving on..\n", ((*a)->x));
//recursive call by passing next two nodes
rev_rec_dll(b, &((*b)->next));
}
else
{
printf("reached new head\r\n");
head = (*a);
}
}
void add_node(Node **h_node, uint8_t x)
{
//check if there is at least one node in the list and if not add first node
if((*h_node) == NULL)
{
*h_node = (Node *)malloc(sizeof(Node));
(*h_node)->x = x;
(*h_node)->next = NULL;
(*h_node)->prev = NULL;
}
else
{
Node *temp = *h_node;
//get the last node
while(temp->next != NULL)
{
temp = temp->next;
}
//add new node
Node *newNode = (Node *)malloc(sizeof(Node));
newNode->x = x;
newNode->next = NULL;
newNode->prev = temp;
temp->next = newNode;
}
}
void display_nodes(Node *h_node)
{
while(h_node != NULL)
{
printf("Node: %u\n", h_node->x);
h_node = h_node->next;
}
}
int main(int argc, char **argv)
{
//add three nodes
add_node(&head, 1);
add_node(&head, 2);
add_node(&head, 3);
//display nodes
display_nodes(head);
//add three more nodes
add_node(&head, 4);
add_node(&head, 5);
add_node(&head, 6);
//display all 6 nodes
display_nodes(head);
//reverse the linked list
rev_rec_dll(&head, &(head->next));
//display reversed nodes
display_nodes(head);
return 0;
}
The output of the program is given below:
Node: 1
Node: 2
Node: 3
Node: 1
Node: 2
Node: 3
Node: 4
Node: 5
Node: 6
done for node 1 and moving on..
I want to know what is wrong in the function rev_rec_dll(). Also, I want to know if the way I am passing the arguments to this function is correct or not. If it is not correct please provide appropriate reason on why it is wrong. The arguments passed to rev_rec_dll function is current node and next node in the linked list.
The reversing logic may not be accurate but I want to know if the way the arguments are passed is correct. Why does it exit in the middle? A memory violation?
For starters it is a bad idea to declare the pointer to head node as a file scope variable and when the functions depend on the file scope variable.
Node *head = NULL;
You should move this declaration inside the function main and correspondingly rewrite your functions.
Also the structure declaration will look better if to declare it like
typedef struct Node
{
uint8_t x;
struct Node *next;
struct Node *prev;
} Node;
Otherwise the names nodes and Node will only confuse readers of the code.
There are two problems with the function. The first one is that initially the second function argument points to the data member next of the head node
rev_rec_dll(&head, &(head->next));
that is changed within the function in the compound statement of the if statement.
To make it clear consider a list that contains only two nodes
| NULL <- 1 -> 2 | <-> | 1 <- 2 -> NULL|
Inside the first recursive call of the function there are executed the following statements
(*a)->next = (*a)->prev;
(*a)->prev = (*b);
Again pay attention to that the function is called like
rev_rec_dll(&head, &(head->next));
That is the parameter b of the function points to the data member next of the head node and the first of the above statements changes the value of the data member
(*a)->next = (*a)->prev;
So now the expression *b yields data member next that now contains the value of the data member prev (in this case NULL).
In fact you have
(*a)->next = (*a)->prev;
(*a)->prev = ( *a )->next; //(*b);
because the expressions ( *a )->next and *b are equivalent due to initializations of parameters by argument expressions.
That is the both data members. next and prev, get the initial value of the data member prev and you have
| NULL <- 1 -> NULL | <- | 1 <- 2 -> NULL|
Then the function is called like
rev_rec_dll(b, &((*b)->next));
that invokes undefined behavior because the expression *b yields a null pointer.
Another problem is that in this code snippet
else
{
printf("reached new head\r\n");
head = (*a);
}
data members prev and next of the last node in the list are not changed because the control is bypassed the compound statement of the if statement
And moreover your function will invoke undefined behavior if it will
be called for an empty list.
The function should be declared with only one parameter like for example
void rev_rec_dll( Node **head );
Here is a demonstration program that shows how the recursive function that reverses the list can be implemented.
In the demonstration program there is not used a file scope pointer to the head node. In this case within a program you will be able to define multiple lists.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
typedef struct Node
{
uint8_t x;
struct Node *next;
struct Node *prev;
} Node;
void clear( Node **head )
{
while (*head)
{
Node *current = *head;
*head = ( *head )->next;
free( current );
}
}
size_t assign( Node **head, const uint8_t *a, size_t n )
{
clear( head );
size_t cnt = 0;
for ( Node *prev = NULL; n-- && ( *head = malloc( sizeof( Node ) ) ) != NULL; cnt++)
{
( *head )->x = *a++;
( *head )->next = NULL;
( *head )->prev = prev;
prev = *head;
head = &( *head )->next;
}
return cnt;
}
void rev_rec_dll( Node **head )
{
if ( *head )
{
Node *tmp = ( *head )->next;
( *head )->next = ( *head )->prev;
( *head )->prev = tmp;
if (( *head )->prev != NULL)
{
*head = ( *head )->prev;
rev_rec_dll( head );
}
}
}
void display_nodes( const Node *head )
{
for (; head != NULL; head = head->next)
{
printf( "%" PRIu8 " -> ", head->x );
}
puts( "null" );
}
int main( void )
{
Node *head = NULL;
uint8_t a[] = { 1, 2, 3, 4, 5, 6 };
assign( &head, a, sizeof( a ) / sizeof( *a ) );
display_nodes( head );
rev_rec_dll( &head );
display_nodes( head );
clear( &head );
}
The program output is
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> null
6 -> 5 -> 4 -> 3 -> 2 -> 1 -> null
There's no real point of using pointer to a pointer in rev_rec_dll(Node **a, Node **b), i would suggest you read into practical implications of pointers to pointers
What you can do here is just use regular pointers like so:
void rev_rec_dll(Node* a, Node* b)
{
//check if it is last node in the list
if(a->next != NULL)
{
//set next to prev and prev to next
a->next = a->prev;
a->prev = b;
printf("done for node %d and moving on..\n", a->x);
//recursive call by passing next two nodes
rev_rec_dll(b, b->next);
}
else
{
printf("reached new head\r\n");
head = a;
}
}
then in main function call it like so
rev_rec_dll(head, head->next);
also, as Vlad mentioned you shouldn't use two parameters in this function, it adds unnecessary complexity as well as makes it a bit harder to track if you pass and use NULL in the future.
also also, it's best that you learn how to use debugger now to see where things hit the fan clearly.
I need a little help removing unique characters in a doubly linked list in C. So here's the logic I tried implementing: I counted the occurrence of each character in the doubly linked list. If it's occurrence is 1 time, then it is unique element and needs to be deleted. I'll be repeating the process for all elements. But my code in remove_unique_dll() function isn't working properly, please help me fix it. Here's my code-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node
{
char data;
struct node *next;
struct node *prev;
};
struct node *head, *tail = NULL; //Represent the head and tail of the doubly linked list
int len;
void addNode(char data)
{
struct node *newNode = (struct node*) malloc(sizeof(struct node)); //Create new node
newNode->data = data;
if (head == NULL)
{ //If dll is empty
head = tail = newNode; //Both head and tail will point to newNode
head->prev = NULL; //head's previous will point to NULL
tail->next = NULL; //tail's next will point to NULL, as it is the last node of the list
}
else
{
tail->next = newNode; //newNode will be added after tail such that tail's next points to newNode
newNode->prev = tail; //newNode's previous will point to tail
tail = newNode; //newNode will become new tail
tail->next = NULL; //As it is last node, tail's next will point to NULL
}
}
void remove_unique_dll()
{
struct node *current = head;
struct node *next;
struct node *prev;
int cnt;
while (current != NULL)
{
next = current->next;
cnt = 1;
//printf("!%c ",next->data);
while (next != NULL)
{
if (next->data == current->data)
{
cnt += 1;
next = next->next;
}
else
next = next->next;
//printf("#%c %d %c\n",next->data,cnt,current->data);
}
if (cnt == 1)
{
prev = current->prev;
//printf("#%c %d",prev->data,cnt);
if (prev == NULL)
{
head = next;
}
else
{
prev->next = next;
}
if (next == NULL)
{
tail = prev;
}
else
{
next->prev = prev;
}
}
current = current->next;
//printf("#%c ",current->data);
}
head = current;
}
void display()
{
struct node *current = head; //head the global one
while (current != NULL)
{
printf("%c<->", current->data); //Prints each node by incrementing pointer.
current = current->next;
}
printf("NULL\n");
}
int main()
{
char s[100];
int i;
printf("Enter string: ");
scanf("%s", s);
len = strlen(s);
for (i = 0; i < len; i++)
{
addNode(s[i]);
}
printf("Doubly linked list: \n");
display();
remove_unique_dll();
printf("Doubly linked list after removing unique elements: \n");
display();
return 0;
}
The output is like this-
If you uncomment the printf() statements inside remove_unique_dll() you'll notice that no code below inner while loop is being executed after inner while loop ends. What's the issue here and what's the solution?
Sample input- aacb
Expected output- a<->a<->NULL
Some issues:
You shouldn't assign head = current at the end, because by then current is NULL
The next you use in the deletion part is not the successor of current, so this will make wrong links
As you progress through the list, every value is going to be regarded as unique at some point: when it is the last occurrence, you'll not find a duplicate anymore, as your logic only looks ahead, not backwards.
When you remove a node, you should free its memory.
Not a big issue, but there is no reason to really count the number of duplicates. Once you find the first duplicate, there is no reason to look for another.
You should really isolate the different steps of the algorithm in separate functions, so you can debug and test each of those features separately and also better understand your code.
Also, to check for duplicates, you might want to use the following fact: if the first occurrence of a value in a list is the same node as the last occurrence of that value, then you know it is unique. As your list is doubly linked, you can use a backwards traversal to find the last occurrence (and a forward traversal to find the first occurrence).
Here is some suggested code:
struct node* findFirstNode(char data) {
struct node *current = head;
while (current != NULL && current->data != data) {
current = current->next;
}
return current;
}
struct node* findLastNode(char data) {
struct node *current = tail;
while (current != NULL && current->data != data) {
current = current->prev;
}
return current;
}
void removeNode(struct node *current) {
if (current->prev == NULL) {
head = current->next;
} else {
current->prev->next = current->next;
}
if (current->next == NULL) {
tail = current->prev;
} else {
current->next->prev = current->prev;
}
free(current);
}
void remove_unique_dll() {
struct node *current = head;
struct node *next;
while (current != NULL)
{
next = current->next;
if (findFirstNode(current->data) == findLastNode(current->data)) {
removeNode(current);
}
current = next;
}
}
You have at least three errors.
After counting the number of occurrences of an item, you use next in several places. However, next has been used to iterate through the list. It was moved to the end and is now a null pointer. You can either reset it with next = current->next; or you can change the places that use next to current->next.
At the end of remove_unique_dll, you have head=current;. There is no reason to update head at this point. Whenever the first node was removed from the list, earlier code in remove_unique_dll updated head. So it is already updated. Delete the line head=current;.
That will leave code that deletes all but one occurrence of each item. However, based on your sample output, you want to leave multiple occurrences of items for which there are multiple occurrences. For that, you need to rethink your logic in remove_unique_dll about deciding which nodes to delete. When it sees the first a, it scans the remainder of the list and sees the second, so it does not delete the first a. When it sees the second a, it scans the remainder of the list and does not see a duplicate, so it deletes the second a. You need to change that.
Let's consider your code step by step.
It seems you think that in this declaration
struct node *head, *tail = NULL; //Represent the head and tail of the doubly linked list
the both pointers head and tail are explicitly initialized by NULL. Actually only the pointer tail is explicitly initialized by NULL. The pointer head is initialized implicitly as a null pointer only due to placing the declaration in file scope. It to place such a declaration in a block scope then the pointer head will be uninitialized.
Instead you should write
struct node *head = NULL, *tail = NULL; //Represent the head and tail of the doubly linked list
Also it is a very bad approach when the functions depend on these global variables. In this case you will be unable to have more than one list in a program.
Also the declaration of the variable len that is used only in main as a global variable
int len;
also a bad idea. And moreover this declaration is redundant.
You need to define one more structure that will contain pointers head and tail as data members as for example
struct list
{
struct node *head;
struct node *tail;
};
The function addNode can invoke undefined behavior when a new node can not be allocated
void addNode(char data)
{
struct node *newNode = (struct node*) malloc(sizeof(struct node)); //Create new node
//...
You should check whether a node is allocated successfully and only in this case change its data members. And you should report the caller whether a node is created or not.
So the function should return an integer that will report an success or failure.
In the function remove_unique_dll after this while loop
while (next != NULL)
{
if (next->data == current->data)
{
cnt += 1;
next = next->next;
}
else
next = next->next;
//printf("#%c %d %c\n",next->data,cnt,current->data);
}
if cnt is equal to 1
if (cnt == 1)
//..
then the pointer next is equal to NULL. And using the pointer next after that like
if (prev == NULL)
{
head = next;
}
else
{
prev->next = next;
}
is wrong.
Also you need to check whether there is a preceding node with the same value as the value of the current node. Otherwise you can remove a node that is not a unique because after it there are no nodes with the same value.
And this statement
head = current;
does not make sense because after the outer while loop
while (current != NULL)
the pointer current is equal to NULL.
Pay attention that the function will be more useful for users if it will return the number of removed unique elements.
Here is a demonstration program that shows how the list and the function remove_unique_dll can be defined.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node
{
char data;
struct node *next;
struct node *prev;
};
struct list
{
struct node *head;
struct node *tail;
};
int addNode( struct list *list, char data )
{
struct node *node = malloc( sizeof( *node ) );
int success = node != NULL;
if (success)
{
node->data = data;
node->next = NULL;
node->prev = list->tail;
if (list->head == NULL)
{
list->head = node;
}
else
{
list->tail->next = node;
}
list->tail = node;
}
return success;
}
size_t remove_unique_dll( struct list *list )
{
size_t removed = 0;
for ( struct node *current = list->head; current != NULL; )
{
struct node *prev = current->prev;
while (prev != NULL && prev->data != current->data)
{
prev = prev->prev;
}
if (prev == NULL)
{
// there is no preceding node with the same value
// so the current node is possibly unique
struct node *next = current->next;
while (next != NULL && next->data != current->data)
{
next = next->next;
}
if (next == NULL)
{
// the current node is indeed unique
struct node *to_delete = current;
if (current->prev != NULL)
{
current->prev->next = current->next;
}
else
{
list->head = current->next;
}
if (current->next != NULL)
{
current->next->prev = current->prev;
}
else
{
list->tail = current->prev;
}
current = current->next;
free( to_delete );
++removed;
}
else
{
current = current->next;
}
}
else
{
current = current->next;
}
}
return removed;
}
void display( const struct list *list )
{
for (const node *current = list->head; current != NULL; current = current->next)
{
printf( "%c<->", current->data );
}
puts( "null" );
}
int main()
{
struct list list = { .head = NULL, .tail = NULL };
const char *s = "aabc";
for (const char *p = s; *p != '\0'; ++p)
{
addNode( &list, *p );
}
printf( "Doubly linked list:\n" );
display( &list );
size_t removed = remove_unique_dll( &list );
printf( "There are removed %zu unique value(s) in the list.\n", removed );
printf( "Doubly linked list after removing unique elements:\n" );
display( &list );
}
The program output is
Doubly linked list:
a<->a<->b<->c<->null
There are removed 2 unique value(s) in the list.
Doubly linked list after removing unique elements:
a<->a<->null
You will need at least to write one more function that will free all the allocated memory when the list will not be required any more.
im trying to insert node to the begging of link list but the value from poiter to poiter isnt passing
i wrote a note ***** where the problem accure
void insertTolist(list* head, list* node)
{
list* current = head;
list* currentPlusOne = head->next;
while (current->next != NULL)
{
if (current->data<node->data && currentPlusOne->data>node->data)
{
current->next = node;
node->next = currentPlusOne;
break;
}
if (current->data<node->data && currentPlusOne->next == NULL)
{
current->next = node;
node->next = (list*)calloc(1, sizeof(list));
break;
}
if (current->data > node->data && currentPlusOne->data >node->data)// b c
{
node->next =current;
head = node;// ***the head doesnt chanching at the main***
break;
}
current = current->next;
currentPlusOne = currentPlusOne->next;
}
//printlist(head);
}
The function declared like
void insertTolist(list* head, list* node)
deals with a copy of the value of the pointer to the head node used as an argument. Changing the copy in this statement
head = node;
is not reflected on the value of the original pointer.
Moreover the function can invoke undefined behavior if the passed pointer is a null pointer at least due to this declaration
list* currentPlusOne = head->next;
Also this statement
node->next = (list*)calloc(1, sizeof(list));
does not make a sense.
You need either to pass the pointer to the head node to the function by reference through a pointer to it or to return from the function the pointer (possibly modified) to the head node and to assign its value to the original pointer.
If to use the first approach then the function will look enough simple.
void insertTolist( list **head, list *node )
{
while ( *head != NULL && !( node->data < ( *head )->data ) )
{
head = &( *head )->next;
}
node->next = *head;
*head = node;
}
and if in the caller the pointer head is declared like
list *head = NULL;
then the function is called like
insertTolist( &head, node );
where node is a pointer to the inserted node in the list.
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
I'm writing a program that creates a doubly linked list and removes a element with negative value from it. Everything pretty much works, except for the part when I called the modify function and when I try to delete it, program crashes. Any suggestions?
/*
*Given a doubly linked lists with +ve and -ve key values.
*Write a function to delete all the nodes with negative key values.
*/
#include<stdio.h>
#include<stdlib.h>
struct list {
int data;
struct list *next;
struct list *prev;
};
struct list *head = NULL;
struct list* create(int);
void modify(struct list*);
int main(void) {
int n, i, value;
struct list *temp;
printf("Enter the count of node :");
scanf("%d",&n);
for (i = 0; i < n; i ++) {
printf("Enter the value of node: ");
scanf("%d",&value);
create(value);
}
temp = head;
printf("\nDoubly linked list is created and the list is as follows : \n");
while (temp != NULL) {
printf("%d ",temp -> data);
temp = temp -> next;
}
modify(head);
}
struct list* create(int value) {
struct list *new_node, *temp;
temp = head;
new_node = (struct list*)malloc(sizeof(struct list));
new_node -> data = value;
new_node -> next = NULL;
new_node -> prev = NULL;
if (head == NULL) {
head = new_node;
}
else {
while (temp -> next != NULL) {
temp = temp -> next;
}
temp -> next = new_node;
new_node -> prev = temp;
}
return head;
}
void modify(struct list *head) {
struct list *current_node, *prev_node, *next_node, *temp;
temp = head;
while (temp -> next != NULL) {
if (temp -> data < 0) {
current_node = temp;
prev_node = temp -> prev;
next_node = temp -> next;
prev_node -> next = next_node;
next_node -> prev = prev_node;
free(current_node);
}
}
printf("\nThe modified doubly linked list is : \n ");
temp = head;
while (temp -> next != NULL) {
printf("%d",temp -> data);
temp = temp -> next;
}
}
See the examples of Vlad from Moscow to have a better understanding of what you were doing.
I shall go trough your code and tell you what I would change.
/*
*Given a doubly linked lists with +ve and -ve key values.
*Write a function to delete all the nodes with negative key values.
*/
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
First of all: you're making a (doubly linked) list of nodes, not a list of lists. Call it a Node. Also, you can do a typedef to prevent you from writing struct Node all the time.
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
void append(struct Node** head, int value); // variable names aren't needed here
struct Node* findLastNode(struct Node** head);
void removeNegativeNodes(struct Node** head);
void removeNode(struct Node** head, struct Node* currNode);
int main(void)
{
Try not to use global variables. There are many reasons to be found why not to use them, but in here it's possible to don't use them as well. Imagine having thousands of lines of code, you won't be able to have a decent view on the code.
struct Node* head = NULL;
struct Node* p; // temp-<p>ointer
int n, value;
printf("Enter the count of node :");
scanf("%d", &n);
You only need i in the for-loop, so keep it there.
for (int i = 0; i < n; ++i) {
printf("Enter the value of node: ");
scanf("%d", &value);
Make sure your function names are clear and tell you what they do. create() would tell me it creates a Node, but not that it also appends the node.
append(&head, value);
}
// this can be in a function! (A) printData
p = head; // temp-<p>ointer
printf("\nDoubly linked list is created and the list is as follows : \n");
while (p != NULL) {
printf("%d <=> ", p->data);
p = p->next;
}
printf("NULL\n");
Look at what you're doing: perhaps you want to make a general function to split the code? Here you're again going trough the list and printing out it's data members.
// this can be in a function! (B) printData
removeNegativeNodes(&head);
printf("\nThe modified doubly linked list is : \n");
p = head;
while (p != NULL) {
printf("%d <=> ", p->data);
p = p->next;
}
printf("NULL\n");
}
struct Node* findLastNode(struct Node** head)
{
struct Node* p = *head;
if (p != NULL)
while (p->next != NULL)
p = p->next;
return p;
}
Since your head has to be changed, you'll have to pass the address of the head as well. Also, split your code a bit, so it's easier for yourself to have an idea of your code's structure. If your function is 40 rules long, it will take longer to find out where the cause of the bug is located (exactly).
void append(struct Node** head, int value)
{
struct Node* lastNode = findLastNode(head);
struct Node* nextNode = (struct Node*)malloc(sizeof(struct Node));
if (lastNode != NULL) {
lastNode->next = nextNode;
nextNode->prev = lastNode;
}
else {
*head = nextNode;
nextNode->prev = NULL;
}
nextNode->next = NULL;
nextNode->data = value;
}
Here as well: the first number can be negative, so make sure you can access the head variable by it's address. Also, again keep it simple and split your code in functions removeNegativeNodes > removeNode.
void removeNegativeNodes(struct Node** head)
{
struct Node* p = *head;
struct Node* temp;
while (p != NULL) {
temp = p->next;
if (p->data < 0)
removeNode(head, p);
p = temp;
}
}
void removeNode(struct Node** head, struct Node* currNode)
{
if (currNode->next != NULL)
currNode->next->prev = currNode->prev;
if (currNode->prev != NULL)
currNode->prev->next = currNode->next;
else
*head = currNode->next;
free(currNode);
}
I've tested the code and it should work. Having it worked properly is not important though, it's understanding what happens. I recommend you having a closer look to it. Goodluck!
Your definition of a doubly-linked list does not make great sense.
The list should contain two pointers: to the head node and to the tail node of the list.
So you need to define two structures. The first one defines the node and the second one defines the list itself.
In this case you need not to traverse the whole list to append a new node to the tail of the list.
The function create with the confusing name is based on the global variable head while the function modify instead gets the variable through a parameter.
This is very confusing. As result for example you can not create two lists in a program.
So as the function modify gets the pointer to the head node by value then it means that it deals with a copy of the pointer to the head node. As a result any changes of the pointer to the head node in the function does not influence on the original pointer to the head node.
This loop in the function modify
temp = head;
while (temp -> next != NULL) {
in general can invoke undefined behavior because it is not excluded that the pointer to the head node can be equal to NULL.
And in any case the condition of the loop does not make sense because within the loop you are considering not the next node but the current
while (temp -> next != NULL) {
if (temp -> data < 0) {
So a question arises if temp->next is equal to NULL but the value of the current node pointed to by the pointer temp is negative does it mean that this node will not be removed?
Pay attention to that if you will write the condition of the loop correctly nevertheless either data member prev of the removed node or the data member next of the removed node or even the both can be equal to NULL. In this case these statements
prev_node = temp -> prev;
next_node = temp -> next;
prev_node -> next = next_node;
^^^^^^^^^^^^^^^^^
next_node -> prev = prev_node;
^^^^^^^^^^^^^^^^^
again can invoke undefined behavior.
here is a demonstrative program that shows how the list and its functions can be defined. Investigate it.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct Node
{
int data;
struct Node *next;
struct Node *prev;
};
struct List
{
struct Node *head;
struct Node *tail;
};
int push_back( struct List *list, int data )
{
struct Node *new_node = malloc( sizeof( struct Node ) );
int success = new_node != NULL;
if ( success )
{
new_node->data = data;
new_node->next = NULL;
if ( list->head == NULL )
{
new_node->prev = NULL;
list->head = list->tail = new_node;
}
else
{
new_node->prev = list->tail;
list->tail = list->tail->next = new_node;
}
}
return success;
}
void remove_if( struct List *list, int predicate( int ) )
{
struct Node *prev = NULL;
for ( struct Node **current = &list->head; *current != NULL; )
{
if ( predicate( ( *current )->data ) )
{
struct Node *tmp = *current;
if ( ( *current )->next != NULL )
{
( *current )->next->prev = ( *current )->prev;
}
*current = ( *current )->next;
free( tmp );
}
else
{
prev = *current;
current = &( *current )->next;
}
}
list->tail = prev;
}
void display( const struct List *list )
{
for ( const struct Node *current = list->head; current != NULL; current = current->next )
{
printf( "%d -> ", current->data );
}
puts( "null" );
}
void display_reverse( const struct List *list )
{
for ( const struct Node *current = list->tail; current != NULL; current = current->prev )
{
printf( "%d -> ", current->data );
}
puts( "null" );
}
int is_negative( int data )
{
return data < 0;
}
int main(void)
{
struct List list = { .head = NULL, .tail = NULL };
const size_t N = 10;
srand( ( unsigned int )time( NULL ) );
for ( size_t i = 0; i < N; i++ )
{
push_back( &list, rand() % N - N / 2 );
}
display( &list );
display_reverse( &list );
putchar( '\n' );
remove_if( &list, is_negative );
display( &list );
display_reverse( &list );
putchar( '\n' );
return 0;
}
The program output might look like
2 -> 4 -> 3 -> -5 -> 3 -> -3 -> -3 -> -2 -> 0 -> 2 -> null
2 -> 0 -> -2 -> -3 -> -3 -> 3 -> -5 -> 3 -> 4 -> 2 -> null
2 -> 4 -> 3 -> 3 -> 0 -> 2 -> null
2 -> 0 -> 3 -> 3 -> 4 -> 2 -> null
The create() function returns a linked list item. so you have to assign the return value to an item. Also the definition of pointers inside the struct is completely wrong.
struct list {
int data;
struct list *next;
struct list *prev;
};
struct list *head = NULL;
struct list* create(int); //function prototype
void modify(struct list*);//function prototype
int main(void) {
int n, i, value;
struct list *temp;
printf("Enter the number of nodes :");
scanf("%d",&n);
for (i = 0; i < n; i ++) {
printf("Enter the value of node: ");
scanf("%d",&value);
create(value);
}
temp = head;
printf("\nDoubly linked list is created and the list is as follows : \n");
while (temp != NULL) {
printf("%d ",temp -> data);
temp = temp -> next;
}
modify(head);
}
void create(int value) {
struct list* point = head;
while(point->next){
if(point->data != value)
point = point->next;
else{
printf("Data exists\n");
return NULL;
}
}
struct list* item = (struct list*)malloc(sizeof(struct list));
item->data = value;
item->next = NULL;
item->prev = point;
}
void modify(struct list *head) {
struct list *current_node, *prev_node, *next_node, *temp;
temp = head;
while (temp -> next != NULL) {
if (temp -> data < 0) {
temp->prev->next = temp->next;
temp->next->prev = temp->prev;
free(temp);
}
temp = temp->next;
}
printf("\nThe modified doubly linked list is : \n ");
temp = head;
while (temp -> next != NULL) {
printf("%d",temp -> data);
temp = temp -> next;
}
}
I hope this will work for you.
I am practicing linked list structure while learning pointers and I have problem with appending item in list. Here is my code
#include <stdio.h>
#include <stdlib.h>
typedef struct node node_t;
struct node {
int data;
node_t* next;
};
void append(node_t *head, int data) {
if (head == NULL) {
node_t *node = (node_t*)malloc(sizeof(node_t*));
node->data = data;
node->next = NULL;
head = node;
} else {
node_t *node = (node_t*)malloc(sizeof(node_t*));
node->data = data;
node->next = NULL;
if (head->next == NULL) {
head->next = node;
} else {
node_t *current = head;
while (1) {
if (current->next == NULL) {
current->next = node;
break;
}
current = current->next;
}
}
}
}
int main(void) {
node_t *head = NULL;
append(head, 4);
append(head, 6);
printList(head);
return 0;
}
My code breaks when I do head = node; It doesn't change value of head in main. I think I'm missing something but not sure what.
Thank you in advance
You are passing the pointer head by value in the function append. So the function deals with a copy of the passed to it pointer. Changing the copy does not influence on the original pointer. Either pass it by reference or return updated head from the function.
The first approach is much better.
The function can look the following way
int append( node_t **head, int data )
{
node_t *node = malloc( sizeof( node_t ) );
int success = node != NULL;
if ( success )
{
node->data = data;
node->next = NULL;
while ( *head != NULL ) head = &( *head )->next;
*head = node;
}
return success;
}
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
typedef struct node node_t;
struct node
{
int data;
node_t *next;
};
int append( node_t **head, int data )
{
node_t *node = malloc( sizeof( node_t ) );
int success = node != NULL;
if ( success )
{
node->data = data;
node->next = NULL;
while ( *head != NULL ) head = &( *head )->next;
*head = node;
}
return success;
}
void printList( node_t *head )
{
for ( ; head != NULL; head = head->next )
{
printf( "%d -> ", head->data );
}
puts( "null" );
}
int main(void)
{
node_t *head = NULL;
const int N = 10;
for ( int i = 0; i < N; i++ )
{
append( &head, i );
}
printList( head );
return 0;
}
Its output is
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
It seems the problem is you are passing the head pointer by value, so when you change it inside append(), you're only changing a local variable in that function - as opposed to the head variable within main().
This may be a bit confusing - if you pass a pointer, how can you be passing by value? Well, you might want to have a look at this question:
Is passing pointer argument, pass by value in C++?
... and the bottom line is that append() needs to take a node_t** head, and you'll call it from main with append(&head, 4);. See it working on Coliru.
Also you're allocating sizeof(node_t*) per node. You should be allocating sizeof(node_t).
It doesn't change value of head in main
Nor should it! If the value of head in main changed when you call append(), then your call to printList() would only print the last node in the list, and you'd have no way to refer to the other nodes in the list.
The reason that head isn't changed has been well explained in other answers, i.e. you're passing the head pointer by value. It's important to understand that the head in main() and the head parameter in append() are entirely different variables.
You pass the head of the list by value, so the append function cannot update the pointer in the caller's space, that happens to have the same name head. The head argument in append is a separate variable from the head local variable in main.
You should either pass a pointer to the head node so append can modify it:
void append(node_t **headp, int data) { ...
Or return the possibly modified head node to the caller which will store it back to its own variable:
node_t *append(node_t *head, int data) { ...
In both cases, it is advisable to signal memory allocation failure to the caller. Returning an error code in the first approach is easy, while returning a null pointer in the second approach can work, as long as the caller does not store the return value directly into its head variable, as in case of failure the previous value would be lost.
Here is a modified version with the first approach:
#include <stdio.h>
#include <stdlib.h>
typedef struct node node_t;
struct node {
int data;
node_t *next;
};
// append a new node to the list, return 0 for success, -1 for allocation failure
int append(node_t **headp, int data) {
node_t *node = (node_t *)malloc(sizeof(node_t *));
if (node == NULL)
return -1;
node->data = data;
node->next = NULL;
if (*headp == NULL) {
*headp = node;
} else {
node_t *current = *headp;
while (current->next != NULL) {
current = current->next;
}
current->next = node;
}
return 0;
}
int main(void) {
node_t *head = NULL;
if (append(&head, 4) || append(&head, 6))
printf("node allocation error\n");
printList(head);
// should free the list
return 0;
}