I need to remove a node from a singly linked list. I know this is a simple thing to do, but my mind is blank and I've searched both Google and Stackoverflow, but I seriously haven't found anything that will help me.
basically the list of nodes is contained in a bucket; like this:
struct node{
unsigned char id[20];
struct node *next;
};
struct bucket{
unsigned char id;
struct node *nodes;
};
and I have a function
struct bucket *dht_bucketfind(unsigned char *id); // return bucket with id[20]
to find the correct bucket. So I know how to find the correct bucket, but I don't know how to remove a given node. I would like to remove the node by nodeid (I think, I haven't really written the code that will call the remove function yet ;) but I think I'll be able to modify the code if necessary). I think that's all that's needed to solve this. Thanks in advance.
If you know the item you want to remove, you must do two things:
Change all pointers that point to the target item to point to the target item's next member. This will be the preceding item's next pointer, or the head of the list bucket.nodes.
Free the node you just made unreachable.
The code for manipulating a linked list is really not that tricky, once you understand what you are doing.
Your nodes don't have any payload other than an id, so, depending on the data payload of a node, you might not actually need to iterate the list in the standard way. This is useful if deleters are going to know the address of only the node they want to delete.
If your payload is a pointer to other data:
struct _node {
void *data;
unsigned char id[20];
struct _node *next
}
Then you could "delete" a node by stealing the payload of the next node, and then delinking the next node:
int delete (struct _node *node)
{
struct _node *temp;
memcpy(node->id, node->next->id, 20);
free_function(node->data);
node->data = node->next->data;
temp = node->next;
node->next = node->next->next);
free(temp);
return 0;
}
/* define your two pointers, prev and cur */
prev=NULL;
cur=head;
/* traverse the list until you find your target */
while (cur != NULL && cur->id != search_id) {
prev=cur;
cur=cur->next;
}
/* if a result is found */
if (cur != NULL) {
/* check for the head of the list */
if (prev == NULL)
head=cur->next;
else
prev->next=cur->next;
/* release the old memory structure */
free(cur);
}
public void Remove(T data)
{
if (this.Head.Data.Equals(data))
{
this.Head = this.Head.Next;
this.Count = this.Count - 1;
}
else
{
LinkedListNode<T> node = this.Head;
bool found = false;
while (node.Next != null && !found)
{
if (node.Next.Data.Equals(data))
{
found = true;
node.Next = node.Next.Next;
this.Count = Count - 1;
}
else
{
node = node.Next;
}
}
}
}
Its been a long time ago since I worked with C, but this should be compile clean.
Basically, you need to keep track of the previous pointer while you iterate through the linked list. When you find the node to delete, just change the previous pointer to skip the delete node.
This function deletes all nodes with id (find). If you want to delete only the first occurrence, then put a return after the free statement.
void delete(struct bucket *thisBucket, unsigned char find[20]) {
struct node *prev = null;
struct node *curr = thisBucket->nodes;
while (curr != null) {
if (!strcmp(curr->id, find)) { // found the node?
if (prev == null) { // going to delete the first node (header)?
thisBucket->nodes = curr->next; // set the new header to the second node
} else {
prev->next = curr->next;
}
free(curr);
// if deleted the first node, then current is now the new header,
// else jump to the next
curr = prev == null? thisBucket->nodes : prev->next;
} else { // not found, keep going
prev = curr;
curr = curr->next;
}
}
}
The following does not contain any error checking and only removes the current node from the list ...
pPrev->next = pCurrent->next;
Your preferences may vary, but I tend to put my linked list node at the start of the structure (whenever practical).
struct node{
struct node *next;
unsigned char id[20];
};
struct bucket{
struct node *nodes;
unsigned char id;
};
I find this generally helps to simplify pointer arithmetic and allows simple typecasting when needed.
This removes a node given its address; you can modify it to remove a node given its id, but you haven't specified the form of an id -- is it a NUL-terminated string, or is it 20 bytes?
// remove node from bucket and return true
// or return false if node isn't in bucket
int dht_rmnode(struct bucket* bucket, struct node* node)
{
struct node** ppnode = &bucket->nodes;
for( ;; ){
struct node* pnode = *ppnode;
if( pnode == NULL ) return 0;
if( pnode == node ){
*ppnode = pnode->next;
return 1;
}
ppnode = &pnode->next;
}
}
Or, more compactly,
// remove node from bucket and return true
// or return false if node isn't in bucket
int dht_rmnode(struct bucket* bucket, struct node* node)
{
struct node** ppnode = &bucket->nodes;
struct node* pnode;
for( ; (pnode = *ppnode); ppnode = &pnode->next )
if( pnode == node ){
*ppnode = pnode->next;
return 1;
}
return 0;
}
typedef struct node
{
int id;
struct node* next;
}Node;
void delete_element(void)
{
int i;
Node* current=head;
Node* brev=NULL;
if(i==head->id){
head=current->next;
free(current);}
else{
while(NULL!=current->next)
{
if(i==current->next->id){
brev=current;
current=current->next;
break;}
else
current=current->next;
}
if(i==current->id)
{
if(NULL==head->next){
head=NULL;
free(current);}
else
brev->next=current->next;
free(current);
}
else
printf("this id does not exist\n");
}
}
Related
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.
Here is the function which getting me into troubles and I actually can't understand why.
This function supposes to removes all the odd elements from a given linked-list and also returns an address of a new linked list of the odd elements removed.
typedef struct node {
int data;
struct node* next;
} Node;
Node* removeOddValues(Node **source)
{
Node *curr = *source;
Node *even;
Node *even_curr;
Node *odd;
Node *odd_curr;
Node *next;
even->next=NULL;
odd->next=NULL;
even_curr = even;
odd_curr = odd;
while(curr)
{
next = curr->next;
curr->next = NULL;
if(curr->data % 2!=0)// odd//
odd_curr = odd_curr->next = curr;//node add to last//
else //even//
even_curr = even_curr->next = curr;
curr = next;
}
*source= even->next;//update source//
return odd->next; //the new list of odd elements removed//
}
When I try to compile it, I get the following error:
warning C4700: uninitialized local variable 'even' used
warning C4700: uninitialized local variable 'odd' used
Two things:
First, you get warnings (and your program contains undefined behaviour and would probably crash) because you access/dereference uninitialised variables:
Node *even;
Node *odd;
even->next=NULL; // even has not been initialised
odd->next=NULL; // odd has not been initialised
Second, your code does not "remember" the roots of the new lists, i.e. you manage odd_curr and even_curr, each pointing to the last node of the respective list, but you do not have something like odd_root and even_root.
The following code shows how this could work. The logic for appending a node at the end while additionally considering a root node is the same for both lists, odd and even, and therefore factored out into a separate function:
void appendNode(Node **root, Node** lastNode, Node *curr) {
if (!*root) { // root not yet assigned?
*root = curr;
*lastNode = curr;
} else {
(*lastNode)->next = curr; // append curr after lastNode
*lastNode = curr; // let curr become the lastNode
}
(*lastNode)->next = NULL; // terminate the list at lastNode
}
Node* removeOddValues(Node **source)
{
Node *curr = *source;
Node *evenRoot = NULL;
Node *oddRoot = NULL;
Node *evenLast = NULL;
Node *oddLast = NULL;
while(curr)
{
Node *next = curr->next;
if(curr->data % 2!=0) {
appendNode(&oddRoot, &oddLast, curr);
}
else {
appendNode(&evenRoot, &evenLast, curr);
}
curr = next;
}
*source= evenRoot;
return oddRoot;
}
curr = next;
Gives you the first warning, because next is not initialized.
odd_curr = odd;
Gives you the second warning, because odd is not initialized.
You should consider using malloc to allocate structures, because you are just allocating pointers, not the actual nodes. You can start learning more about linked lists from this interactive guide. Even if you somehow fix these two warnings, it still won't work.
The simplest case, using only two variables. The even nodes stay in the original list; the odd nodes are removed from the chain and returned as a linked list.
#include <stdio.h>
struct llist {
struct llist * next;
int data;
};
struct llist *chop_odds(struct llist **source)
{
struct llist *odd = NULL;
struct llist **target = &odd;
while ( *source) {
if((*source)->data %2) { /* odd */
*target = *source; // steal the pointer
*source = (*source)->next; // source skips this node
target = &(*target)->next; // advance target
}
else source = &(*source)->next; /* even */
}
*target = NULL;
return odd;
}
Note: I already got the intended function to work with my own code, but I saw a tutorial on another website and am wondering why it doesn't work.
https://www.eskimo.com/~scs/cclass/int/sx8.html
The premise is as follows:
I'm playing around with a very basic linked list:
typedef struct node {
int val;
struct node * next;
} node_t;
I am trying to have a function remove an entry by value. It is as follows:
int remove_by_value(node_t ** head, int val) {
for(head = &node_t; *head != NULL; head = &(*head)->next){
if ((*head)->val == val) {
*head = (*head)->next;
break;
}
}
}
However, I'm getting an error when calling this function, namely:
"prog.c:35:17: error: expected expression before 'node_t'
for(head = &node_t; *head != NULL; head = &(*head)->next){
^"
Any ideas? Is this just a simple syntax error that I'm not seeing? Thanks!
the root of the problem is that node_t is a type, not a variable and cannot take the address of a type.
The following code cleanly compiles.
be sure to check the logic,
for first iteration of the loop when head = NULL or only one struct in linked list
check logic for when desired struct is either last or next to last in the linked list
here is the code:
typedef struct node
{
int val;
struct node * next;
} node_t;
int remove_by_value(node_t ** head, int val)
{
int retVal = -1; // initialize to failed
node_t *previousNode = *head;
node_t *currentNode = *head;
for(;
previousNode && currentNode; // assure something to test
previousNode = currentNode, // update the pointers
currentNode = currentNode->next )
{
if (currentNode->val == val)
{
previousNode->next = currentNode->next;
retVal = 0; // indicate success
break;
}
}
return retVal;
} // end function: remove_by_value
Since I cannot comment on the accepted answer written by #user3629249:
That code is even worse than the original (except that it would compile).
I'd suggest something like this:
node_t *remove_by_value(node_t **head, int val)
{
node_t *ret = NULL;
for (; *head; head = &((*head)->next))
{
if ((*head)->val == val)
{
ret = *head;
*head = (*head)->next;
break;
}
}
return ret;
}
This code correctly removes the element from the beginning, middle and end of the list. In addition it gives the caller the chance to free the unlinked node.
1) The error you are getting was pointed out by iharob already.
2) I can understand that out of this head =&t_node you want head to point at the head of your list. a static variable may be needed in your file to be able to use this then you can point head correctly
I'm simply trying to delete a node from the linked list and seem to be having trouble. I was wondering if someone could please have a look at what could be wrong? Thanks!
struct ets {
struct node *equip_head;
struct node *member_head;
struct node *loan_head;
const char *equip_fname;
const char *member_fname;
const char *loan_fname;
};
struct node {
void *data; /* Accepts all data, yay */
struct node *next;
};
BOOLEAN deleteMember(struct ets *ets, char MemberID[]) {
struct node *current = ets->member_head;
struct node *tmpNode = current;
struct member_data *member_data = NULL;
while (current != NULL) {
member_data = current->data;
if (strcmp(member_data->MemberID, MemberID) == 0) {
tmpNode = current;
current = current->next;
free(tmpNode->data);
free(tmpNode);
return TRUE;
}
current = current->next;
}
return FALSE;
}
You are not removing the node from list. You can do this to remove a node from a list:
BOOLEAN deleteMember(struct ets *ets, char MemberID[]) {
struct node *current = ets->member_head;
struct node *prev=NULL;
struct member_data *member_data = NULL;
while(current != NULL) {
member_data = current->data;
if(strcmp(member_data->MemberID, MemberID) == 0) {
if(prev==NULL) // removing 1st node
ets->member_head=current->next;
else
prev->next=current->next; // removing current node from list
free(current->data);
free(current);
return TRUE;
}
prev = current;
current = current->next;
}
return FALSE;
}
As you have a single linked list you delete algorythm is broken. Here is what your are currently doing :
locate member to delete. Fine. You have node-1 -> node -> node+1
you delete the member. Why not. But you list would become node-1 -> unallocated node and no way to find next nodes
You should instead test if next node has the correct id to have something like : prev_node(current) -> node_to_delete -> next_node`
Then you can do :
tmpNode = current->next;
current->next = tmpNode->next; /* ok for the chaining */
free(tmpNode->data); /* deletion will be ok */
free(tmpNode);
With of course a special management for first and last nodes ...
Edit : Ali already gave the answer. I leave this one as a comment on why OP's algo was broken
I am trying to make a doubly linked list that loops around, so the last link is connected to the first one.
However, I cannot figure out what I am doing wrong with the backlink, as I can print my list forwards but not backwards.
Any tip/help would be much appreciated.
This is my structure definition:
struct NODE {
union {
int nodeCounter;
void *dataitem;
} item;
struct NODE *link;
struct NODE *backlink;
};
//function to create a list
struct NODE *InitList() {
struct NODE *temp = (struct NODE*)malloc(sizeof NODE);
temp->item.nodeCounter = 0;
temp->link = NULL;
temp->backlink = NULL;
return temp;
}
This is my insert function:
void Add2List(struct NODE *start, struct NODE *NewNode) {
struct NODE *current = start;
while (current->link != NULL && current->link != start) {
current = current->link;
}
current->link = NewNode;
NewNode->link = start;
NewNode->backlink = current;
start->backlink = NewNode;
start->item.nodeCounter++;
}
and this is my print backwards function:
void PrintBackwards(struct NODE *start) {
struct NODE * current = start;
while(current->backlink != start) {
DisplayNode((struct inventory*)current->item.dataitem);
current = current->backlink; //go one node back
}
}
The rest of your functions look reasonable but there are least two mistakes in your PrintBackwards function.
If you had intended to print it starting at the end, you should be starting at start->backlink, not at start.
You should not be checking for NULL in the while loop because your list is circular, so there should not be NULL.
The code below fixes those two mistakes, but I am not sure if there are others.
void PrintBackwards(struct NODE *start)
{
if(start == NULL || start->backlink == NULL)
return;
struct NODE * current = start->backlink;
while(current->backlink != start)
{
DisplayNode((struct inventory*)current->item.dataitem);
current = current->backlink; //go one node back
}
}
Maybe....
while(current->backlink != start)
{
DisplayNode((struct inventory*)current->item.dataitem); //dangerous
current = current->backlink; //go one node back
}