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
Related
I divided the code in two files, .h and .c
The definition of function names is in .h, the implementation of the function is in .c
in my main file:
struct no
{
tipo info;
struct no *ant;
struct no *nxt;
};
struct list
{
no_t *head;
no_t *tail;
int size;
};
this is in my .h file:
typedef struct no no_t;
typedef struct list list_t;
typedef int tipo;
...again in main
void list_destroy(list_t **l)
{
if ((*l) == NULL || l == NULL)
return;
if (!(*l)->head)
return;
no_t *next = (*l)->head; //create two variables for iterating through the list
no_t *aux; //set aux to free
while (next->nxt) //the pointer for next node, in the last node, is NULL
{ //by that I believe I'm able to iterate through all nodes
aux = next;
free(aux);
next = next->nxt;
}
free(*l);
(*l) = NULL;
}
is quite a simple code, but I can't see where I'm missing here
next = next->nxt;
For the compiler it makes no difference, for sure. But for someone, even you, it is hard to read this next = next->nxt stuff. Or is it is not?
A possible alternative (using your code) and a short test program
so_list.h
#include <stdio.h>
#include <stdlib.h>
typedef int Tipo;
typedef struct st_no
{
Tipo info;
struct st_no* prev;
struct st_no* next;
} Node;
typedef struct
{
Node* head;
Node* tail;
unsigned size;
} List;
List* list_create();
List* list_destroy(List*);
int list_insert(const Tipo, List*);
In the header, only typedefs and the function prototypes.
names with only the first letter in uppercase are reserved here for defined names. An useful convention.
instead of using List** is often clearer to just return the pointer to the list. In this way it is easier for example to invalidate the pointer and to create the linked lists as in
List* my_list = list_create();
my_list = list_destroy(my_list);
and there is no need to test the two levels of indirection as you need when ** is used
main.c: a minimalist test set
#include "so-list.h"
int main(void)
{
List* my_list = list_create();
my_list = list_destroy(my_list);
my_list = list_create();
for (int i = 1; i <= 5; i += 1)
printf("insert(%d,list) returned %d\n",
i, list_insert(i,my_list)
);
my_list = list_destroy(my_list);
my_list = list_create();
for (int i = 11; i <= 15; i += 1)
printf("insert(%d,list) returned %d\n",
i, list_insert(i, my_list)
);
my_list = list_destroy(my_list);
return 0;
}
A list is created, then destroyed
using the same pointer, a list is created, values 1 to 5 are inserted ant then the list is deleted.
using the same pointer, a list is created, values 11 to 15 are inserted ant then the list is again deleted.
the output
List created!
List deleted!
List created!
insert(1,list) returned 1
insert(2,list) returned 2
insert(3,list) returned 3
insert(4,list) returned 4
insert(5,list) returned 5
1 deleted
2 deleted
3 deleted
4 deleted
5 deleted
List deleted!
List created!
insert(11,list) returned 1
insert(12,list) returned 2
insert(13,list) returned 3
insert(14,list) returned 4
insert(15,list) returned 5
11 deleted
12 deleted
13 deleted
14 deleted
15 deleted
List deleted!
code for destroy_list()
List* list_destroy(List* l)
{
if (l == NULL) return NULL;
// delete the ´size´ nodes, 1 by 1
Node* p = NULL;
for (unsigned i = 0; i < l->size; i += 1)
{
p = l->head->next; // save pointer
printf("%d deleted\n", l->head->info); // just for the demo
free(l->head); // free head
l->head = p; // advance head
}
free(l); // free list
printf("List deleted!\n\n"); // just for the demo
return NULL;
}
This function always return NULL as just a way to invalidade the pointer in the caller in the same expression as in pList = destroy_list(pList);
This is somewhat different than the code you wrote. We just delete the elements one by one as we know the list has size elements. A local pointer is used in the loop to save the address of the next element. It seems to be easier to read.
The complete code for so-list.c
#include "so-list.h"
List* list_create()
{
List* one = (List*)malloc(sizeof(List));
one->head = NULL;
one->tail = NULL;
one->size = 0;
printf("List created!\n");
return one;
}
List* list_destroy(List* l)
{
if (l == NULL) return NULL;
// delete the ´size´ nodes, 1 by 1
Node* p = NULL;
for (unsigned i = 0; i < l->size; i += 1)
{
p = l->head->next; // save pointer
printf("%d deleted\n", l->head->info);
free(l->head); // free head
l->head = p; // advance head
}
free(l); // free list
printf("List deleted!\n\n");
return NULL;
}
// just for test, insert ´info´ at the end, returns size
int list_insert(const Tipo info, List* l)
{
// insert node at the end, just for test
Node* one = (Node*)malloc(sizeof(Node));
one->info = info;
one->next = NULL;
one->prev = l->tail;
if (l->size == 0)
l->head = one; // 1st node
else
l->tail->next = one;
l->tail = one;
l->size += 1;
return l->size;
};
about your version of list_destroy()
The logic there is a bit wrong but the error is well described in another answer. I recommend not to use ** in this situations. But it can be done for sure.
so-list.c
This is just a minimum to have a running test
#include "so-list.h"
List* list_create()
{
List* one = (List*)malloc(sizeof(List));
one->head = NULL;
one->tail = NULL;
one->size = 0;
printf("List created!\n");
return one;
}
List* list_destroy(List* l)
{
if (l == NULL) return NULL;
// delete the ´size´ nodes, 1 by 1
Node* p = NULL;
for (unsigned i = 0; i < l->size; i += 1)
{
p = l->head->next; // save pointer
printf("%d deleted\n", l->head->info);
free(l->head); // free head
l->head = p; // advance head
}
free(l); // free list
printf("List deleted!\n\n");
return NULL;
}
// just for test, insert ´info´ at the end, returns size
int list_insert(const Tipo info, List* l)
{
// insert node at the end, just for test
Node* one = (Node*)malloc(sizeof(Node));
one->info = info;
one->next = NULL;
one->prev = l->tail;
if (l->size == 0)
l->head = one; // 1st node
else
l->tail->next = one;
l->tail = one;
l->size += 1;
return l->size;
};
This has an issue
no_t *next = (*l)->head;
no_t *aux;
while (next->nxt)
{
aux = next; // aux point to the same object as next
free(aux); // free aux, which is the same as next
next = next->nxt; // deference next, which just got free'd. OOPS!
}
You invoke free on aux, which is also aliasing next. Then you try to deference next->nxt. Well, next just got released in the previous statement. Also, as I called out in the comment, you are leaking the last element in the list.
Fixed:
no_t* aux = (*l)->head;
while (aux)
{
no_t* next = aux->nxt;
free(aux);
aux = next;
}
You should look to your "free" and your "next->nxt" statements. May it can help you solve it.
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.
I am doing an exercise with a linked list, in which I need to remove some nodes according to a condition. The condition is ''remove the nodes that have a data stored that is less or equal to 'avg''.
I should consider three cases:
- removing the head node
- removing a node in the middle
- removing last node
I used three pointers, but this doesn't seem to work. What am I doing wrong? Am I accidentally missing some pointers? Thank you in advance!
void checkAvg(int avg, struct list* head_node){
struct list* prev;
struct list* curr;
struct list* next;
prev = head;
curr = prev->link;
next = curr->link;
while (curr!=NULL) {
if (prev->data <= avg) {
head = curr;
}
if (curr->data <= avg) {
prev->link = curr->link;
}
if (next->data <= avg) {
curr->link = next->link;
}
prev = prev->link;
curr = curr->link;
next = next->link;
}
}
EDIT:
I am sorry, as you told me, I wasn't specific. The program has to do this:
1) Read a linked list
2) Generate an output average of the values I read (sum of values / # of values)
3) remove from the list the nodes that have a data that is <= to the average
Free the memory of those nodes.
About the free() part, I have some difficulties with that, so I thought I first had to learn how to properly use pointers. As you may have noticed, I am not really good at that, I am starter.
Thank you for everyone who has replied, I am checking the replies right now
If your linked list does not have some sentinel head-node (and I strongly advise that it does not, as NULL is a damn fine value to indicate "I'm empty"), removal traversal is not overtly complicated. The places in your code where you seem to go astray are:
Passing the head pointer by-address and declaring the parameter as a pointer-to-pointer OR returning the new head pointer if the old one was disposed.
Maintaining consistency in your local pointer variables. You have to know for sure what everything points to at all times.
You can use pointer values, or you can use the actual pointers themselves (by address). I prefer the latter. In either case, the head node pointer must be passed by-address to allow potential modification of the caller's variable (like everything else in C) or the function can return the potentially new head node address. I prefer the former of those methods, as it leaves you the option of using the function return value for communicating error states to your caller. You're not doing that right now, but you should (hint).
void checkAvg(int avg, struct list** pp)
{
while (*pp)
{
if ((*pp)->data <= avg)
{
struct list *victim = *pp;
*pp = victim->link;
free(victim);
}
else
{ // advance to address of next "link" pointer
pp = &(*pp)->link;
}
}
}
It is worth noting this can be significantly simpler if the list is maintained as sorted in ascending order. If that is the case, the entire checkAvg function becomes substantially simpler. You can exit the loop as soon as you detect a value that no longer fits your condition:
void checkAvg(int avg, struct list** pp)
{
while (*pp && (*pp)->data <= avg)
{
struct list *victim = *pp;
*pp = victim->link;
free(victim)
}
}
In either case, the function is invoked by passing the head pointer on the caller-side by-address:
struct list *head = NULL;
//... populate list....
checkAvg(value, &head);
How it Works
A linked list is just something that looks like this:
-------- -------- --------
head ---> | link | ---> | link | ---> | link | ---> NULL
| val1 | | val2 | | val3 |
-------- -------- --------
Using the methods posted, traversing the list uses a pointer-to-pointer, which does something like this:
pp --:
: -------- -------- --------
head ---> | link | ---> | link | ---> | link | ---> NULL
| val1 | | val2 | | val3 |
-------- -------- --------
pp points to a pointer; not a node. Initially pp holds the address of the head pointer (was passed in by-address as a parameter).
So what happens if the first node matches your criteria? Ultimately, this is the result
pp --:
: -------- --------
head ---> | link | ---> | link | ---> NULL
| val2 | | val3 |
-------- --------
--------
victim ---> | link |
| val1 |
--------
and the victim node is summarily discarded (the link of victim actually still references the second node, but is meaningless in this context after the detachment happens.
So, what about if the second node was the one that needed removal (i.e. we skipped the first node). That gets a little more complicated:
pp -----:
:
---:---- --------
head ---> | link | ---> | link | ---> NULL
| val1 | | val3 |
-------- --------
--------
victim ---> | link |
| val2 |
--------
What this is trying to show (admittedly poorly) is that the pp pointer-to-pointer always holds the address of the pointer that we may be modifying. If we don't need to modify that pointer, we change the address held in pp to point to the next link pointer in the list (obtained through pp = &(*pp)->link
The latter case doesn't happen when the list is already sorted , thus the reason its iterative loop is simpler. We just enumerate the list, throwing out nodes until we find one that no longer meets our condition.
But no matter what, pp always holds the address of the pointer that points to the node we're working with.. Initially, that address is the address of the caller's head pointer.
Ok. I can only hope that makes it clearer. Some debugging with printf("pp=%p, *pp=%p\n", pp, *pp) in the loop makes what is actually happening most-educational. Walking through the algorithms by-hand in a debugger would be highly informative.
It's a lot easier than you think.
You're walking and modifying a linked list, so set up a current and previous.
void checkAvg(int avg, struct list** head_node){ //when calling, use checkAvg(avg, &head_node);
struct list* prev = NULL;
struct list* curr = *head_node;
Starting at the head...
while(curr != NULL){
if(curr->data <= avg){
if(prev == NULL){
*head_node = curr->next; //updates the head node
} else {
prev->next = curr->next; //removes the unwanted node
}
}
curr = curr->next;
}
You really don't need a special end case because the while loop terminates when curr is NULL; for the last item in the list, curr->next is NULL so when it get set it will end the loop. You also don't need to check if the list is empty, because the loop will end if it curr == NULL and you assign the head to curr at first.
I want some recursive magic tonight
Node* deleteBad(Node *head, int(*pred)(int)) {
if (head == NULL) {
return NULL;
}
if (pred(head->value)) {
head->next = deleteBad(head->next, pred);
return head;
}
else {
Node *next = head->next;
free(head);
return deleteBad(next, pred);
}
}
And accumulators
void deleteBad2(Node *head, int(*pred)(int), Node *out) {
if (head) {
if (pred(head->value)) {
out->next = head;
deleteBad2(head->next, pred, out->next);
} else {
out->next = head->next;
deleteBad2(head->next, pred, out);
}
}
}
And for those coxcombs who dislike recursion optimized version
void deleteBad3(Node *head, int(*pred)(int), Node *out) {
begin:
if (head) {
if (pred(head->value)) {
out->next = head;
out = out->next;
head = head->next;
goto begin;
}
else {
out->next = head->next;
head = head->next;
goto begin;
}
}
}
and if someone dislike gotos
void deleteBad3nogoto(Node *head, int(*pred)(int), Node *out) {
while (1) {
if (head) {
if (pred(head->value)) {
out->next = head;
out = out->next;
head = head->next;
}
else {
out->next = head->next;
head = head->next;
}
}
else {
break;
}
}
}
You're checking each node three times in each iteration, which leads to some weird behavior. Check the head before entering the loop, and only compare the current node each time:
void checkAvg(int avg, struct list* head_node){
struct list* prev;
struct list* curr;
while (head!=NULL) {
if (head->data <= avg) {
head = head->link;
} else {
break;
}
}
prev = head;
curr = prev->link;
while (curr!=NULL) {
if (curr->data <= avg) {
prev->link = curr->link;
}
prev = prev->link;
curr = prev->link;
}
}
Rather than checking the data of prev, curr and next, just check for one single data in the loop.
You also need to return the new head in case it changes.
I'd suggest something like that (tested):
#include <stdio.h>
#include <malloc.h>
typedef struct list_ {
struct list_ *link;
int data;
} list;
list* RemoveElementsBelow(int avg, list* head_node) {
while (head_node != NULL && head_node->data <= avg) {
// Remove the first.
list* new_head = head_node->link;
free(head_node);
head_node = new_head;
}
if (head_node == NULL) {
return NULL;
}
list* prev;
list* curr;
prev = head_node;
curr = prev->link;
while (curr != NULL) {
if (curr->data <= avg) {
prev->link = curr->link; // Will be NULL at the end of the list.
free(curr);
curr = prev->link;
} else {
prev = curr;
curr = curr->link;
}
}
return head_node;
}
list* AddToHead(int value, list* head_node) {
list* new_node = malloc(sizeof(list));
new_node->link = head_node;
new_node->data = value;
return new_node;
}
list* PrintAndDeleteList(list* head_node) {
while (head_node != NULL) {
list* new_head = head_node->link;
printf("%d ", head_node->data);
free(head_node);
head_node = new_head;
}
printf("\n");
}
int main(int argc, char **argv) {
list* my_list = NULL;
int i;
int sum = 0;
for (i = 1; i < argc; ++i) {
int value = atoi(argv[i]);
sum += value;
my_list = AddToHead(value, my_list);
}
if (argc == 1) {
return 1;
}
int avg = sum / (argc - 1);
printf("Average value: %d\n", avg);
my_list = RemoveElementsBelow(avg, my_list);
PrintAndDeleteList(my_list);
return 0;
}
Compiled:
gcc -o test test.c
Tested:
./test 10 20 30 40 50
Average value: 30
50 40
./test 50 40 30 20 10
Average value: 30
40 50
Write a function that rearranges a linked list to put the nodes in even positions after the nodes in odd positions in the list, preserving the relative order of both the evens and the odds.
I found this problem in the book Algorithm in c writtern by Sedgewick. I have tried but failed. I trid to put all nodes in even positions on another linked list. It's grateful for you to help me. A good idea is enough. Thanks :).
This is my Code in C.
/*
* File: rearranges.c <Exercise 3.36>
* Note: Write a function that rearranges a linked list to put the nodes in even
* positions after the nodes in odd positions in the list, preserving the
* relative order of both the evens and the odds.
* NOTICE: I think it's necessary to use linked list with a dummy head.
* Time: 2013-10-26 10:58
*/
#include <stdio.h>
#include <stdlib.h>
#define LEN 11
typedef struct node *link;
struct node {
int item;
link next;
};
/* Traverse a linked list with a dummy head. */
void traverse(link t) {
link x = t->next;
while (x != NULL) {
printf("%d ", x->item);
x = x->next;
}
putchar('\n');
}
/* Detach even positon nodes from a linked list. */
link detach(link t) {
link u = malloc(sizeof(*u));
link x = t, y = u;
/* x is odd position node. We should ensure that there's still one even
* position node after x. */
while (x != NULL && x->next != NULL) {
y->next = x->next;
x->next = x->next->next;
x = x->next;
y = y->next;
y->next = NULL;
}
return u;
}
/* Combine two linked list */
link combine(link u, link t) {
link x = u;
link y = t->next;
while (y != NULL) {
link n = y->next;
y->next = x->next;
x->next = y;
x = x->next->next;
y = n;
}
return u;
}
/* The function exchanges the position of the nodes in the list. */
link rearranges(link t) {
link u = detach(t);
link v = combine(u, t);
return v;
}
int main(int argc, char *argv[]) {
int i;
link t = malloc(sizeof(*t));
link x = t;
for (i = 0; i < LEN; i++) {
x->next = malloc(sizeof(*x));
x = x->next;
x->item = i;
x->next = NULL;
}
traverse(t);
traverse(rearranges(t));
return 0;
}
curr=head;
end=lastOfList;//last node if size of list is odd or last-1 node
for(int i=1;i<=listSize()/2;i++)
{
end->next=curr->next;
end=end->next;
end->next=null;
if(curr->next!=null)
if((curr->next)->next!=null)
curr->next=(curr->next)->next;
curr=curr->next;
}
You can implement a recursive solution where each call returns an updated node that will serve as the new next reference for the upper caller. We just have to go down the list until we find the last element, and then move every even node to the end of the list, and update the reference to the last element. Here's my solution (please try to do it yourself before looking at my and other solutions):
struct node {
int val;
struct node *next;
};
struct node *reorder_aux(struct node *l, int count, struct node **last);
struct node *reorder(struct node *l) {
struct node *x;
if (l == NULL)
return NULL;
return reorder_aux(l, 1, &x);
}
struct node *reorder_aux(struct node *l, int count, struct node **last) {
struct node *n;
if (l->next == NULL) {
*last = l;
return l;
}
n = reorder_aux(l->next, count+1, last);
if (count & 1) {
l->next = n;
return l;
}
else {
(*last)->next = l;
l->next = NULL;
*last = l;
return n;
}
}
At each step, if the current node l is an even node (as determined by count), then we append this node to the end, and tell the upper caller that its next pointer shall be updated to our next (because our next will be an odd node). In case we're an odd node, we just have to update our next pointer to whatever the recursive call returned (which will be a pointer to an odd node), and return the current node, since we will not move ourselves to the end of the list.
It's a nice exercise!
#include <stdio.h>
struct list {
struct list *next;
int ch;
};
void swap_odd_even (struct list **pp)
{
struct list *one, *two ;
for( ; (one = *pp) ; pp = &one->next) {
two = one->next;
if (!two) break;
*pp = two;
one->next = two->next;
two->next = one;
}
}
struct list arr[] =
{ {arr+1, 'A'} , {arr+2, 'B'} , {arr+3, 'C'} , {arr+4, 'D'}
, {arr+5, 'E'} , {arr+6, 'F'} , {arr+7, 'G'} , {arr+8, 'H'}
, {arr+9, 'I'} , {arr+10, 'J'} , {arr+11, 'K'} , {arr+12, 'L'}
, {arr+13, 'M'} , {arr+14, 'N'}, {arr+15, 'O'} , {arr+16, 'P'}
, {arr+17, 'Q'} , {arr+18, 'R'} , {arr+19, 'S'} , {arr+20, 'T'}
, {arr+21, 'U'} , {arr+22, 'V'}, {arr+23, 'W'} , {arr+24, 'X'}
, {arr+25, 'Y'} , {NULL, 'Z'} };
int main (void) {
struct list *root , *ptr;
root = arr;
for (ptr=root ; ptr; ptr = ptr->next ) {
printf( "-> %c" , ptr->ch );
}
printf( "\n" );
printf( "Swap\n" );
swap_odd_even ( &root);
for (ptr=root ; ptr; ptr = ptr->next ) {
printf( "-> %c" , ptr->ch );
}
printf( "\n" );
return 0;
}
In the following, every time swap_nodes is called another odd sinks to the last sunk odd. The evens are grouped together on each iteration and they bubble up to the end of the list. Here is an example:
/*
[0]-1-2-3-4-5
1-[0-2]-3-4-5
1-3-[0-2-4]-5
1-3-5-[0-2-4]
*/
#include <stdio.h>
#include <stdlib.h>
#define LIST_LENGTH 10
struct node{
int id;
struct node *next;
};
void print_list(struct node *current)
{
while(NULL != current){
printf("node id = %d\n",current->id);
current = current->next;
}
printf("Done\n");
}
struct node *swap_nodes(struct node *head_even, struct node *tail_even, struct node *next_odd)
{
tail_even->next = next_odd->next;
next_odd->next = head_even;
return next_odd;
}
struct node *reorder_list(struct node *head)
{
struct node *head_even;
struct node *tail_even;
struct node *next_odd;
struct node *last_odd;
if(NULL == head->next){
return head;
}
head_even = head;
tail_even = head;
next_odd = head->next;
last_odd = head->next;
head = swap_nodes(head_even, tail_even, next_odd);
if(NULL != tail_even->next){
tail_even = tail_even->next;
}
while (NULL != tail_even->next) {
next_odd = tail_even->next;
last_odd->next = swap_nodes(head_even, tail_even, next_odd);
last_odd = last_odd->next;
if(NULL != tail_even->next){
tail_even = tail_even->next;
}
}
return head;
}
int main(void)
{
int i;
struct node *head = (struct node *) malloc(LIST_LENGTH*sizeof(struct node));
struct node *mem = head;
if(NULL == head){
return -1;
}
struct node *current = head;
for(i=0;i<LIST_LENGTH-1;i++){
current->next = current + 1;
current->id = i;
current = current->next;
}
current->next = NULL;
current->id = i;
head = reorder_list(head);
print_list(head);
free(mem);
return 0;
}
I have a simple question about removing an element from a linked list. The only difference between what I am trying to accomplish and what I have seen in code online is that I am trying to remove an element, given a position, rather than given the actual element that needs to be removed.
Any help is appreciated.
You can do that, Here is the sample program which can delete any node based on the index you are supplying as an argumenT.
start -> Pointing to the first node
traverse ->pointing to the node which has to be deleted
traverseNext->pointing to the previous of node which has to be deleted.
And the code looks like below.
#include <iostream>
struct myList
{
int data;
struct myList *next;
};
struct myList *start=NULL;
//this method removes a node from the position index
void remove(int index)
{
myList *traverse = start;
myList *traverseNext = NULL;
int i = 1;
while(i<(index-1))
{
traverse = traverse->next;
i++;
}
traverseNext = traverse;
traverse = traverse->next;
if(traverse->next == NULL)
{
delete traverse;
traverseNext->next = NULL;
traverse = NULL;
return;
}
else
{
traverseNext->next = traverse->next;
delete traverse;
traverse = NULL;
return;
}
}
int main(void)
{
myList *node1;
myList *node2;
myList *node3;
node1 = new myList;
node2 = new myList; //Created 3 nodes of type myList
node3 = new myList;
node1->data = 10;
node1->next = node2;
node2->data = 20;
node2->next = node3;
node3->data = 30;
node3->next = NULL;
start = node1; //start is pointing to node1
remove(2); //removing the node 2, so the output will be 10 30
while(start) //iterating through all the nodes from start, since start
{ //is pointing to the first node.
std::cout<<start->data<<" ";
start = start->next;
}
}
Deleting a node in a linked list to which the pointer is given can be done in O(1) time. We don't have to do traversal.
I am assuming that by position you meant the pointer to the node is given:
Lets say node is the element that needs to be removed.
node->data = node->next->data;
Node* temp = node->next;
node->next = node->next->next;
free(temp);
But if the position means the nth element in the list, the only way would be to traverse up to the (n-1)th element and delete the next element by (regular deletion in a linked list):
Node* temp = previous->next;
previous->next = temp->next;
free(temp);
This is all assuming that the linked-list is a pointer based linked-list
This is simple:
1) Locate the Nth item by iterating through the list, additionally using a counter to keep track of which node you're one.
2) Remove that node, as you would any other linked list.
Seek the list until you find the nth element (use a counter), and then update the previous node's next pointer to point to the one after the one you're currently at (effectively removing it). Adjust the previous pointer if you're using them, too.
If you want to remove multiple items you can first iterate over the list and then collect all items that you want to remove to another list. Then simply call 'removeAll' passing in the collected list.
#include <stdio.h>
#include <stdlib.h>
typedef struct element{
int num;
struct element * next;
} element;
void delNth(element **header, int pos){//pos : zero origin
element *prev, *tmp;
int i;
if(header == NULL || *header == NULL || pos < 0) return ;
if(pos == 0){
tmp = (*header)->next;
free(*header);
*header = tmp;
} else {
prev = *header;
tmp = (*header)->next;
for(i=1;i<pos;++i){
prev = tmp;
tmp = tmp->next;
if(tmp == NULL) return ;//or rise error
}
prev->next = tmp->next;
free(tmp);
}
}
void drop(element *header){
if(header){
drop(header->next);
free(header);
}
}
void printList(element *header){
while (header!=NULL){
printf("%d ",header->num);
header=header->next;
}
printf("\n");
}
int main(int argc, char **argv){
int pos = atoi(argv[1]);
element *a;
element *b;
element *c;
a=malloc(sizeof(element));
b=malloc(sizeof(element));
c=malloc(sizeof(element));
a->num=5;
b->num=6;
c->num=7;
a->next=b;
b->next=c;
c->next=NULL;
printList(a);
delNth(&a, pos);
printList(a);
drop(a);
return 0;
}
/* execute result
>a 0
5 6 7
6 7
>a 1
5 6 7
5 7
>a 2
5 6 7
5 6
>a 3
5 6 7
5 6 7
*/