How would you iterate this 2D linked list?
typedef struct _NODE
{
char *pszName;
unsigned long ulIntVal;
char *pszString;
struct _NODE *pNext;
struct _NODE *pDown;
} NODE;
I could do something like this..
NODE *pHEad;
while (pHead != NULL) {
printf("%s", pHead->pDown->pszName);
pHead = pHead->pNext;
}
.. but it would only give me the one node under every next node. What if it is another node under that one again? And under that one again? Or if there is a pNext attached to the pDown?
In the simplest case, you could use something like the following recursive function:
void processNode(NODE *current) {
if (current != NULL) {
printf("%s", current->pszName);
processNode(current->pNext);
processNode(current->pDown);
}
}
int main(void) {
NODE *pHead;
/* ... Do something to fill your list ... */
processNode(pHead);
/* ... */
}
Also be aware that this can cause a deep nesting of the function calls depending on your processed list. So if you are on an embedded system with limited stack size or if you are processing huge lists, you might run out of stack. In that case, you should find another approach for the processing.
Note that this will first process the pNext-list and then start with processing the first node of the pDown-list of the last node. So assuming the following structure (to the right is pNext and downwards is pDown):
pHead -> p1 -------> p2
|- p1_1 |- p2_1 -> p2_1_1
\- p1_2 |- p2_2
\- p2_3 -> p2_3_1
it should print the nodes in the following order:
pHead, p1, p2, p2_1, p2_1_1, p2_2, p2_3, p2_3_1, p1_1, p1_2
Look at this answer. Don't be overwhelmed by the amount of the code. I have added enough comments to help you proceed.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node{
char data[100]; // Assume that this linked list will contain only 100 chars of data
struct Node* next;
} NODE;
// Global Variables are bad, but oh well.
NODE* head = NULL;
// Function to create a node
NODE* createNode(char* str)
{
// First allocate memory for struct
NODE* newNode = malloc(sizeof(NODE));
if(newNode == NULL)
{
printf("Unable to create a new node.");
}
else
{
// Use strcpy or strncpy or memcpy instead of doing something like newNode -> data = str, which changes the pointer, but doesn't copy the contents
// That is do not do newNode -> data = "hello" or something
strncpy(newNode -> data, str, strlen(str));
newNode -> next = NULL;
}
return newNode;
}
void addNode(char* str)
{
// Returns a node which contains str, but points to NULL
NODE* newNode = createNode(str);
// If the linked list is empty, then we make this node itself as the first node(or head)
if(head == NULL)
{
head = newNode;
}
// Else if the linked list is not empty, then we add this node at the start of the linked list
else
{
newNode -> next = head;
head = newNode;
}
}
int main()
{
// Example Linked List Generated(say you already have it in some form)
addNode("This");
addNode("Is");
addNode("Linked List");
// Now let's print the linked list
// Temporary NODE pointer ptr is used in order to not mess with the original NODE pointer head.
NODE* ptr = head;
// Traverse through the linked list starting from head and at the same time printing the corresponding data, until ptr is null
// This ptr != NULL check is exactly what you are looking for. This is your way of stopping the traversal of Linked List once you
// are at the end of it. You don't have to know the number of nodes to stop the traversal this way.
while(ptr != NULL)
{
printf("%s ", ptr -> data);
ptr = ptr -> next;
}
}
However note that the output will be printed in reverse order, since in this implementation of linked list we are adding things towards the back. Just try running the program and start reading the program starting from main function. I have made the code into separate functions to make it easier for you to understand. Just run the code first to get a grasp of what's happening.
You can use iteration instead of recursion by adding a queue, too, if you want to avoid the possibility of a stack overflow—though this will use slightly more heap memory, and there is still a risk that you can run out of heap memory if you have a large list or if you're running on a memory-constrained system. The important part is the print_list function at the end; the other stuff is just a (mostly) self-managing queue implementation I've provided:
typedef struct node_queue NodeQueue;
struct node_queue {
NODE *n;
NodeQueue *next;
};
/*
* Add an item to the end of the queue.
*
* If the item could not be added, 0 is returned.
* Otherwise, a nonzero value is returned.
*/
int enqueue(NodeQueue **headp, NodeQueue **endp, NODE *n)
{
NodeQueue *old_end = *endp;
NodeQueue *new_end;
new_end = malloc(sizeof *new_end);
if (new_end == NULL) {
return 0;
}
new_end->n = n;
new_end->next = NULL;
if (old_end != NULL) {
old_end->next = new_end;
}
if (*headp == NULL) {
*headp = new_end;
}
*endp = new_end;
return 1;
}
/*
* Remove an item from the head of the queue,
* storing it in the object that "nret" points to.
*
* If no item is in the queue, 0 is returned.
* Otherwise, a nonzero value is returned.
*/
int dequeue(NodeQueue **headp, NodeQueue **endp, NODE **nret)
{
NodeQueue *old_head = *headp;
NodeQueue *new_head;
if (old_head == NULL) {
return 0;
}
if (nret != NULL) {
*nret = old_head->n;
}
new_head = old_head->next;
free(old_head);
if (new_head == NULL) {
*endp = NULL;
}
*headp = new_head;
return 1;
}
void print_list(NODE *start)
{
NodeQueue *head = NULL;
NodeQueue *end = NULL;
NODE *current;
current = start;
/* Iterate all `pNext` nodes, then pop each `pDown` node and repeat. */
for (;;) {
/* Add the "down" node to the node queue. */
if (current->pDown != NULL) {
if (!enqueue(&head, &end, current->pDown)) {
perror("warning: could not add node to queue");
}
}
printf("%s", current->pszNode);
/*
* Move to the "next" node.
* If there is no next node, get the first "down" node from the queue.
* If there is no "down" node, break the loop to end processing.
*/
current = current->pNext;
if (current == NULL) {
if (!dequeue(&head, &end, ¤t)) {
break;
}
}
}
}
This will iterate through all pNext items before moving to a pDown item. The following 2-D list will be printed as A B C D E F G H I J K L M N O P Q:
A
|
B--C
|
D--E-----------F
| |
G-----H I-----J
| | | |
K--L M--N O P
|
Q
You can reverse the priority of pDown/pNext in the print_list function by swapping pNext and pDown inside it, so pNext items are added to the queue and pDown items are iterated until exhausted, which will change the order in which the items are printed to A B D C E G K F I O H M Q L J P N unless you change the structure of the list.
You can see an example using both the code above and the first sample 2-D linked list above at https://repl.it/NjyV/1, though I changed the definition of NODE to make the code using its fields a bit simpler.
This was an remote coding test I got a few days ago, and I've already submitted the answers so I'm not cheating here.
The question is fairly simple: implement a function to delete an item from a linked list. But with one catch: the linked list is in the form of an array.
typedef struct _Node{
int val;
struct _Node* next;
} Node;
Test input provided by the interviewer, which we can modify slightly
void printLL(Node* root){
Node* current = root;
while (current){
printf("%d\n", current->val);
current = current->next;
}
}
int main(){
Node list[6];
list[0].value =1; list[0].next = list+1;
list[1].value =2; list[1].next = list+2;
list[2].value =3; list[2].next = list+3;
list[3].value =4; list[3].next = list+4;
list[4].value =5; list[4].next = list+5;
list[5].value =6; list[5].next = 0;
delete(&list, 3) // this gives segmentation error with my implementation;
printLL(list);
return 0;
}
My answer, which is standard for linked list deletion:
void delete(Node** root, int val){
Node* current = *root;
Node* prev = NULL;
if ((*root)->val == val){
*root = (*root)->next;
//free(current); // test case not dynamically allocated
return;
}
while (current && (current->val != val)){
prev = current;
current = current->next;
}
if (!current){
printf("value not found\n");
return ;
}
else{
prev->next = current->next;
//free(current);
}
}
However, if I use pointers instead, then the function works
Node* p = list;
delete(&p, 3);
I think I understand the difference between &list and &p as function argument:
**Variable name | Variable value | Variable address**
list | address of first elem | address of first elem
p | address of first elem | some random address of the pointer
But since in the delete function we are operating with *list and *p respectively, their value should also be the same.
My guess now is because of in
*root = (*root)->next;
if *root is an array name, then it's illegal as we cannot reassign it. But if *root is a pointer, then we are freely to reassign them. Am I correct on this?
Thank you for reading through this long and messy post.
I'm implementing circular linked list in C of which the central part is the add2list() function which adds nodes based on the int data field in ascending order. I have 5 different scenarios of when to add the node in the function which makes me think that it's kind of too much. Most of the implementations I've seen in google have 2 or 3 cases. However most of those implementations also use multiple functions for adding a node (separate function for adding a node to the beginning/end of the list). I would appreciate your feedback whether I have cases that can be merged (for example case 2 and 4 are quite similar). I added printList() function if you would like to print the list.
This is my code:
#include <stdio.h>
#include <stdlib.h>
typedef struct node * ptr;
typedef struct node {
int data;
ptr next;
}item;
void add2list(ptr *head, int num);
void printList(ptr p);
int main() {
ptr head = NULL;
add2list(&head, 1);
add2list(&head, 2);
add2list(&head, 5);
add2list(&head, 3);
add2list(&head, 0);
add2list(&head, 4);
add2list(&head, -2);
printList(head);
return 0;
}
void add2list(ptr *head, int num) {
ptr p1, p2, t;
t = (ptr) malloc(sizeof(item));
if(!t) {
printf("not enough memory\n");
exit(0);
}
t -> data = num;
p1 = *head;
while(p1 != NULL && p1 -> next != *head && p1 -> data < num) {
p2 = p1;
p1 = p1 -> next;
}
if(!p1) {
//case 1 - if the list is empty
*head = t;
t -> next = *head;
} else if(p1 == *head) {
if(p1 -> data < t -> data) {
//case 2 - if we need to add a node to the end of the list if there was only one node before
(*head) -> next = t;
t -> next = *head;
} else {
//case 3 - if we need to add a node to the beginning of the list
while(p1 -> next != *head) {
p1 = p1 -> next;
}
p1 -> next = t;
t -> next = *head;
*head = t;
}
} else if(p1 -> data < t -> data){
//case 4 - need to add a node at the end of the list if there's more than one node
p1 -> next = t;
t -> next = *head;
} else {
//case 5 - need to add a node in the middle
p2 -> next = t;
t -> next = p1;
}
}
void printList(ptr head) {
ptr tmp;
tmp = head;
while(tmp -> next != head) {
printf("%d ->\n", tmp -> data);
tmp = tmp -> next;
}
printf("%d ->\n", tmp -> data);
}
Here's my attempt (warning, code is untested):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef struct Node Node;
struct Node {
int data;
Node *next;
};
void add2list(Node **proot, int value) {
Node **pcur;
Node *new;
if (!(new = malloc(sizeof *new))) {
fprintf(stderr, "malloc(%zu): %s\n", sizeof *new, strerror(errno));
exit(EXIT_FAILURE);
}
new->data = value;
if (!*proot) {
// case 1: insert into empty list
new->next = new;
*proot = new;
return;
}
if ((*proot)->data >= value) {
// case 2: insert at beginning of list
pcur = &(*proot)->next;
while (*pcur != *proot) {
pcur = &(*pcur)->next;
}
new->next = *proot;
*proot = *pcur = new;
return;
}
// case 3: insert elsewhere
pcur = &(*proot)->next;
while (*pcur != *proot && (*pcur)->data < value) {
pcur = &(*pcur)->next;
}
new->next = *pcur;
*pcur = new;
}
The function first allocates a new node and sets its data member. This is common to all three cases, as in your code.
Case 1 is insertion into an empty list. This is pretty trivial.
Case 3 is the "normal" case and almost identical to insertion into a non-circular list (the only difference being the test for end-of-list, which involves NULL for a non-circular list). (This subsumes your cases 2, 4, 5.)
Case 2 (insertion at the beginning) is where it gets tricky. This case is special because here we have to update two variables, not just one: The variable in the caller, *proot (because it has to be updated to the new head of the list), as well as the next pointer of the last node in the list (to keep it properly circular). In this case we have an extra loop that walks through the whole list just to find the address of the last next pointer in the list (stored in pcur). At the end we update *proot and *pcur together. (This is your case 3.)
Yes, there are definitely too many cases. You should avoid situations like these one and use a top-down approach whenever possible.
Doing so your code will be cleaner and more maintainable.
Moreover you can reuse functions whenever you need them and if you find a bug inside a function it's a matter of correcting the function to make everything works fine.
I am merging two linked lists and third lists consist of elements at alternate postions but function which will merge is not working
void insert2()
{
//node ptr-pointer of first linked list<br>
//node1 ptr1-pointer of second list<br>
//node2 ptr2 -pointer of the merged linked list
node *ptr=head;
node1 *ptr1=head1;
node2 *ptr2=(node2*)malloc(sizeof(node2));
node *ptr3;
while(ptr!=NULL&&ptr1!=NULL)
{
//Entering the element of first linked list
ptr2->info=ptr->info;
if(head2==NULL)
{
ptr->next=NULL;
head=ptr;
}
else
{
ptr3=head2;
while(ptr3->next!=NULL)
{
ptr3=ptr3->next;
}
ptr3->next=ptr2;
ptr2->next=NULL;
}
//Entering the element of second linked list
ptr2->info=ptr1->info;
while(ptr3->next!=NULL)
{
ptr3=ptr3->next;
}
ptr3->next=ptr2;
ptr2->next=NULL;
}
}
Suppose we are going to add the nodes alternatively in the second list. So the second list will become the new list. The idea is
Keep 2 pointers pointing to current node. Initially the original lists p and q.
While they are not null (both are not null) you store in the next address of both nodes.
Now what will you do? You will point to the next of p to current of q and current of q to next of p.
The current pointers will now be properly changed to the next nodes because they will now be processed.
-------- ----------
| p_curr|------------>| |--------->NULL
|-------| p_next-->|--------|
-------- ----------
| q_curr|------------>| |--------->NULL
|-------| q_next-->|--------|
//After an iteration in the while loop.
-------- ----------
| |----| --> | p_curr| --------->NULL
|-------| _ |______| |--------|
| |
-------- | | ----------
| |_| |------->| q_curr |--------->NULL
|-------| |--------|
The code will be something like this (For more information check link)
void merge(struct node *p, struct node **q)
{
struct node *p_curr = p, *q_curr = *q;
struct node *p_next, *q_next;
// While therre are avialable positions in p
while (p_curr != NULL && q_curr != NULL)
{
// Save next pointers
p_next = p_curr->next;
q_next = q_curr->next;
// Make q_curr as next of p_curr
q_curr->next = p_next; // Change next pointer of q_curr
p_curr->next = q_curr; // Change next pointer of p_curr
// Update current pointers for next iteration
p_curr = p_next;
q_curr = q_next;
}
*q = q_curr; // Update head pointer of second list
}
Creating a third linked list as result
You can do it similar to the previous way. Now what are the changes you nees to have?
Simply you can make a copy of list A and list B and then you pass these as parameters to the list and you will get the third list from the previous function shown above.
But that's quite a primitive solution. You can also build the list in linear time on fly in the function.
struct node * getnode()
{
struct node *temp=(struct node*)malloc(sizeof(struct node));
if(temp==NULL)
{
printf("\n Error in allocation\n");
exit(0);
}
return temp;
}
void merge(struct node *p, struct node *q,struct node **n)
{
struct node *p_curr = p, *q_curr = q;
struct node **store;
struct node *n1;
// While therre are avialable positions in p
while (p_curr != NULL || q_curr != NULL)
{
if (p_curr)
{
if(first)
{
store=&n1;first=0;
}
n1=getnode();
n1->info=p_curr->info;
n1->next = p_curr->next;
n1=n1->next;
p_curr=p_curr->next;
}
if (q_curr)
{
if(first)
{
store=&n1;first=0;
}
n1=getnode();
n1->info=q_curr->info;
n1->next = q_curr->next;
n1=n1->next;
q_curr=q_curr->next;
}
}
*n=*store;
}
Remember in this case the two if statements are checking whether any of them is NULL. If it is the case then add the nodes of other list in the resulting node. store stores the address of the first node. After all we need to point to the head of the third node.
Try this:
NODE * AlternateListMerge(NODE *pSrc1, NODE *pSrc2)
{
NODE *pDst = NULL; /* destination head ptr */
NODE **ppDst = &pDst; /* ptr to head or prev->next */
NODE *pNode; /* ptr to node */
while(pSrc1 || pSrc2){
if(pSrc1){
pNode = malloc(sizeof(NODE));
pNode->info = pSrc1->info;
pSrc1 = pSrc1->next;
*ppDst = pNode;
ppDst = &pNode->next;
}
if(pSrc2){
pNode = malloc(sizeof(NODE));
pNode->info = pSrc2->info;
pSrc2 = pSrc2->next;
*ppDst = pNode;
ppDst = &pNode->next;
}
}
*ppDst = NULL;
return pDst;
}
or this (less code, a bit more time):
NODE * AlternateListMerge(NODE *pSrc1, NODE *pSrc2)
{
NODE *pDst = NULL; /* destination head ptr */
NODE **ppDst = &pDst; /* ptr to head or prev->next */
NODE *pNode; /* ptr to node */
NODE *pSwap; /* used for swap */
while(pSrc1 || pSrc2){
if(pSrc1){
pNode = malloc(sizeof(NODE));
pNode->info = pSrc1->info;
pSrc1 = pSrc1->next;
*ppDst = pNode;
ppDst = &pNode->next;
}
pSwap = pSrc1;
pSrc1 = pSrc2;
pSrc2 = pSwap;
}
*ppDst = NULL;
return pDst;
}
With no declarations of the types involved, no information about how your lists are initialized, and no details about how you are trying to output the result, I can speak only in generalities. Given that you did say you want to avoid modifying the original lists, however, it is certain that you need to make a copy of each node in each list. Here's one way you could do it:
struct node {
void *data;
struct node *next;
};
#define COPY(from, to, error_label) do { \
to = malloc(sizeof(struct node)); \
if (! to) { \
/* allocation error */ \
goto error_label; \
} \
to->data = from->data; \
to->next = NULL; \
} while (0)
int merge(struct node *head1, struct node *head2, struct node **result) {
struct node dummy = { NULL, NULL };
struct node *merge_tail = dummy;
int node_count = 0;
while (head1 || head2) {
if (head1) {
COPY(head1, merge_tail->next, allocation_error);
head1 = head1->next;
merge_tail = merge_tail->next;
node_count += 1;
}
if (head2) {
COPY(head2, merge_tail->next, allocation_error);
head2 = head2->next;
merge_tail = merge_tail->next;
node_count += 1;
}
}
*result = dummy->next;
return node_count;
allocation_error:
while (dummy->next) {
struct node *temp = dummy->next;
dummy->next = dummy->next->next;
free(temp);
}
return -1;
}
The basic idea is that you walk both input lists at the same time, alternatively copying nodes into the output list from one input and from the other. This particular implementation returns a count of the total number of nodes in the merged list, or -1 on error; the head of the merged list is returned via the third argument. It will not be tripped up if the input lists have different lengths (left over nodes from the longer list are appended at the end), or if either or both input lists are empty. Most of the gory details of copying a node are factored out into the COPY() macro. The dummy node avoids the head of the merged list being a special case.
Any way around, it makes no sense to do any of this if the nodes from which the three lists are built are of different types.
In a recent Slashdot Interview Linus Torvalds gave an example of how some people use pointers in a way that indicates they don't really understand how to use them correctly.
Unfortunately, since I'm one of the people he's talking about, I also failed to understand his example:
I've seen too many people who delete a singly-linked list entry by keeping track of the "prev" entry, and then to delete the entry, doing
something like
if (prev)
prev->next = entry->next;
else
list_head = entry->next;
and whenever I see code like that, I just go "This person doesn't
understand pointers". And it's sadly quite common. People who
understand pointers just use a "pointer to the entry pointer", and
initialize that with the address of the list_head. And then as they
traverse the list, they can remove the entry without using any
conditionals, by just doing
*pp = entry->next
Can someone provide a bit more explanation about why this approach is better, and how it can work without a conditional statement?
At the beginning, you do
pp = &list_head;
and, as you traverse the list, you advance this "cursor" with
pp = &(*pp)->next;
This way, you always keep track of the point where "you come from" and can modify the pointer living there.
So when you find the entry to be deleted, you can just do
*pp = entry->next
This way, you take care of all 3 cases Afaq mentions in another answer, effectively eliminating the NULL check on prev.
If you like learning from examples, I prepared one. Let's say that we have the following single-linked list:
that is represented as follows (click to enlarge):
We want to delete the node with the value = 8.
Code
Here is the simple code that do this:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
struct node_t {
int value;
node_t *next;
};
node_t* create_list() {
int test_values[] = { 28, 1, 8, 70, 56 };
node_t *new_node, *head = NULL;
int i;
for (i = 0; i < 5; i++) {
new_node = malloc(sizeof(struct node_t));
assert(new_node);
new_node->value = test_values[i];
new_node->next = head;
head = new_node;
}
return head;
}
void print_list(const node_t *head) {
for (; head; head = head->next)
printf("%d ", head->value);
printf("\n");
}
void destroy_list(node_t **head) {
node_t *next;
while (*head) {
next = (*head)->next;
free(*head);
*head = next;
}
}
void remove_from_list(int val, node_t **head) {
node_t *del, **p = head;
while (*p && (**p).value != val)
p = &(*p)->next; // alternatively: p = &(**p).next
if (p) { // non-empty list and value was found
del = *p;
*p = del->next;
del->next = NULL; // not necessary in this case
free(del);
}
}
int main(int argc, char **argv) {
node_t *head;
head = create_list();
print_list(head);
remove_from_list(8, &head);
print_list(head);
destroy_list(&head);
assert (head == NULL);
return EXIT_SUCCESS;
}
If you compile and run this code you'll get:
56 70 8 1 28
56 70 1 28
Explanation of the code
Let's create **p 'double' pointer to *head pointer:
Now let's analyze how void remove_from_list(int val, node_t **head) works. It iterates over the list pointed by head as long as *p && (**p).value != val.
In this example given list contains value that we want to delete (which is 8). After second iteration of the while (*p && (**p).value != val) loop (**p).value becomes 8, so we stop iterating.
Note that *p points to the variable node_t *next within node_t that is before the node_t that we want to delete (which is **p). This is crucial because it allows us to change the *next pointer of the node_t that is in front of the node_t that we want to delete, effectively removing it from the list.
Now let's assign the address of the element that we want to remove (del->value == 8) to the *del pointer.
We need to fix the *p pointer so that **p was pointing to the one element after *del element that we are going to delete:
In the code above we call free(del), thus it's not necessary to set del->next to NULL, but if we would like to return the pointer to the element 'detached' from the list instead of the completely removing it, we would set del->next = NULL:
Reconnecting the list once a node is to be removed is more interesting. Let's consider at least 3 cases:
1.Removing a node from the beginning.
2.Removing a node from the middle.
3.Removing a node from the end.
Removing from the beginning
When removing the node at the beginning of the list, there is no relinking of nodes to be performed, since the first node has no preceding node. For example, removing node with a:
link
|
v
--------- --------- ---------
| a | --+---> | b | --+---> | c | 0 |
--------- --------- ---------
However, we must fix the pointer to the beginning of the list:
link
|
+-------------+
|
v
--------- --------- ---------
| a | --+---> | b | --+---> | c | 0 |
--------- --------- ---------
Removing from the middle
Removing a node from the middle requires that the preceding node skips over the node being removed. For example, removing the node with b:
link
|
v
--------- --------- ---------
| a | --+--+ | b | --+---> | c | 0 |
--------- | --------- ---------
| ^
+----------------+
This means that we need some way to refer to the node before the one we want to remove.
Removing from the end
Removing a node from the end requires that the preceding node becomes the new end of the list (i.e., points to nothing after it). For example, removing the node with c:
link
|
v
--------- --------- ---------
| a | --+---> | b | 0 | | c | 0 |
--------- --------- ---------
Note that the last two cases (middle and end) can be combined by saying that "the node preceding the one to be removed must point where the one to be removed does."
In the first approach, you delete a node by unlink it from the list.
In the second approach, you replace the to-be-deleted node with the next node.
Apparently, the second approach simplifies the code in an elegant way. Definitely, the second approach requires better understanding of the linked list and the underlying computation model.
Note: Here is a very relevant but slightly different coding question. Good for testing one's understanding:
https://leetcode.com/problems/delete-node-in-a-linked-list/
I prefer the Dummy node approach, an example layout:
|Dummy|->|node1|->|node2|->|node3|->|node4|->|node5|->NULL
^ ^
| |
curr curr->next // << toDel
and then, you traverse to the node to delete (toDel = curr>next)
tmp = curr->next;
curr->next = curr->next->next;
free(tmp);
That way, you don't need to check if it's the first element, because the first element is always Dummy and never gets deleted.
Here's my take, I found it easier to understand this way.
Example of deleting a node in a linked list, using pointers to pointers.
struct node {
int value;
struct node *next;
};
void delete_from_list(struct node **list, int n)
{
struct node *entry = *list;
while (entry && entry->value != n) {
// store the address of current->next value (1)
list = &entry->next;
// list now stores the address of previous->next value
entry = entry->next;
}
if (entry) {
// here we change the previous->next value
*list = entry->next;
free(entry);
}
}
Assuming we create a list with these values:
*node value next
----------------------------------------
a 1 null
b 2 a
c 3 b
d 4 c
e 5 d
If we want to delete the node with value 3:
entry = e
while (entry && entry->value != 3) iterations:
e->value != 3
list = &e->next
entry = d
d->value != 3
list = &d->next
entry = c
c->value == 3
STOP
if (entry)
d->next = b (what was 'list' is dereferenced)
free(entry)
After if (entry) we have:
d->next = b
So the list becomes:
*node value next
----------------------------------------
a 1 null
b 2 a
c 3 b
d 4 b
e 5 d
And finally:
free(entry)
And the list becomes:
*node value next
----------------------------------------
a 1 null
b 2 a
d 4 b
e 5 d
If we want to delete the first node, it will still work, because initially
*list == entry
therefore with:
*list = entry->next;
*list will point to the second element.
(1) Note that saying:
list = &entry->next;
Is the same as saying:
list = &(entry->next);
Here's a complete code example, using a function-call to remove matching elements:
rem() removes matching elements, using prev
rem2() removes matching elements, using pointer-to-pointer
// code.c
#include <stdio.h>
#include <stdlib.h>
typedef struct list_entry {
int val;
struct list_entry *next;
} list_entry;
list_entry *new_node(list_entry *curr, int val)
{
list_entry *new_n = malloc(sizeof(list_entry));
if (new_n == NULL) {
fputs("Error in malloc\n", stderr);
exit(1);
}
new_n->val = val;
new_n->next = NULL;
if (curr) {
curr->next = new_n;
}
return new_n;
}
#define ARR_LEN(arr) (sizeof(arr)/sizeof((arr)[0]))
#define CREATE_LIST(arr) create_list((arr), ARR_LEN(arr))
list_entry *create_list(const int arr[], size_t len)
{
if (len == 0) {
return NULL;
}
list_entry *node = NULL;
list_entry *head = node = new_node(node, arr[0]);
for (size_t i = 1; i < len; ++i) {
node = new_node(node, arr[i]);
}
return head;
}
void rem(list_entry **head_p, int match_val)
// remove and free all entries with match_val
{
list_entry *prev = NULL;
for (list_entry *entry = *head_p; entry; ) {
if (entry->val == match_val) {
list_entry *del_entry = entry;
entry = entry->next;
if (prev) {
prev->next = entry;
} else {
*head_p = entry;
}
free(del_entry);
} else {
prev = entry;
entry = entry->next;
}
}
}
void rem2(list_entry **pp, int match_val)
// remove and free all entries with match_val
{
list_entry *entry;
while ((entry = *pp)) { // assignment, not equality
if (entry->val == match_val) {
*pp = entry->next;
free(entry);
} else {
pp = &entry->next;
}
}
}
void print_and_free_list(list_entry *entry)
{
list_entry *node;
// iterate through, printing entries, and then freeing them
for (; entry != NULL; node = entry, /* lag behind to free */
entry = entry->next,
free(node)) {
printf("%d ", entry->val);
}
putchar('\n');
}
#define CREATELIST_REMOVEMATCHELEMS_PRINT(arr, match_val) createList_removeMatchElems_print((arr), ARR_LEN(arr), (match_val))
void createList_removeMatchElems_print(const int arr[], size_t len, int match_val)
{
list_entry *head = create_list(arr, len);
rem2(&head, match_val);
print_and_free_list(head);
}
int main()
{
const int match_val = 2; // the value to remove
{
const int arr[] = {0, 1, 2, 3};
CREATELIST_REMOVEMATCHELEMS_PRINT(arr, match_val);
}
{
const int arr[] = {0, 2, 2, 3};
CREATELIST_REMOVEMATCHELEMS_PRINT(arr, match_val);
}
{
const int arr[] = {2, 7, 8, 2};
CREATELIST_REMOVEMATCHELEMS_PRINT(arr, match_val);
}
{
const int arr[] = {2, 2, 3, 3};
CREATELIST_REMOVEMATCHELEMS_PRINT(arr, match_val);
}
{
const int arr[] = {0, 0, 2, 2};
CREATELIST_REMOVEMATCHELEMS_PRINT(arr, match_val);
}
{
const int arr[] = {2, 2, 2, 2};
CREATELIST_REMOVEMATCHELEMS_PRINT(arr, match_val);
}
{
const int arr[] = {};
CREATELIST_REMOVEMATCHELEMS_PRINT(arr, match_val);
}
return 0;
}
See the code in action here:
gcc https://wandbox.org/permlink/LxgMddqCZWyj7lMI
clang https://wandbox.org/permlink/5aEkxh24sGfAecwF
If compiling and using valgrind (a memory-leak checker) like this:
gcc -std=c11 -Wall -Wextra -Werror -o go code.c && valgrind ./go
we see that all is good.
#glglgl:
I wrote following simple example. Hope you can have a look why it works.
In function void delete_node(LinkedList *list, void *data), I use *pp = (*pp)->next; and it works. To be honest, I don't understand why it works. I even draw the diagram of pointers but still not understand it. Hope you can clarify it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _employee {
char name[32];
unsigned char age;
} Employee;
int compare_employee(Employee *e1, Employee *e2)
{
return strcmp(e1->name, e2->name);
}
typedef int (*COMPARE)(void *, void *);
void display_employee(Employee *e)
{
printf("%s\t%d\n", e->name, e->age);
}
typedef void (*DISPLAY)(void *);
typedef struct _node {
void *data;
struct _node *next;
} NODE;
typedef struct _linkedlist {
NODE *head;
NODE *tail;
NODE *current;
} LinkedList;
void init_list(LinkedList *list)
{
list->head = NULL;
list->tail = NULL;
list->current = NULL;
}
void add_head(LinkedList *list, void *data)
{
NODE *node = malloc(sizeof(NODE));
node->data = data;
if (list->head == NULL) {
list->tail = node;
node->next = NULL;
} else {
node->next = list->head;
}
list->head = node;
}
void add_tail(LinkedList *list, void *data)
{
NODE *node = malloc(sizeof(NODE));
node->data = data;
node->next = NULL;
if (list->head == NULL) {
list->head = node;
} else {
list->tail->next = node;
}
list->tail = node;
}
NODE *get_node(LinkedList *list, COMPARE compare, void *data)
{
NODE *n = list->head;
while (n != NULL) {
if (compare(n->data, data) == 0) {
return n;
}
n = n->next;
}
return NULL;
}
void display_list(LinkedList *list, DISPLAY display)
{
printf("Linked List\n");
NODE *current = list->head;
while (current != NULL) {
display(current->data);
current = current->next;
}
}
void delete_node(LinkedList *list, void *data)
{
/* Linus says who use this block of code doesn't understand pointer.
NODE *prev = NULL;
NODE *walk = list->head;
while (((Employee *)walk->data)->age != ((Employee *)data)->age) {
prev = walk;
walk = walk->next;
}
if (!prev)
list->head = walk->next;
else
prev->next = walk->next; */
NODE **pp = &list->head;
while (((Employee *)(*pp)->data)->age != ((Employee *)data)->age) {
pp = &(*pp)->next;
}
*pp = (*pp)->next;
}
int main ()
{
LinkedList list;
init_list(&list);
Employee *samuel = malloc(sizeof(Employee));
strcpy(samuel->name, "Samuel");
samuel->age = 23;
Employee *sally = malloc(sizeof(Employee));
strcpy(sally->name, "Sally");
sally->age = 19;
Employee *susan = malloc(sizeof(Employee));
strcpy(susan->name, "Susan");
susan->age = 14;
Employee *jessie = malloc(sizeof(Employee));
strcpy(jessie->name, "Jessie");
jessie->age = 18;
add_head(&list, samuel);
add_head(&list, sally);
add_head(&list, susan);
add_tail(&list, jessie);
display_list(&list, (DISPLAY) display_employee);
NODE *n = get_node(&list, (COMPARE) compare_employee, sally);
printf("name is %s, age is %d.\n",
((Employee *)n->data)->name, ((Employee *)n->data)->age);
printf("\n");
delete_node(&list, samuel);
display_list(&list, (DISPLAY) display_employee);
return 0;
}
output:
Linked List
Susan 14
Sally 19
Samuel 23
Jessie 18
name is Sally, age is 19.
Linked List
Susan 14
Sally 19
Jessie 18