Delete struct node using pointer-to-pointer - c

Suppose that I have a linked list, the next function deletes struct node from the linked list
struct list **lpp;
for (lpp = &list; *lpp != NULL; lpp = &(*lpp)->next)
{
if ((*lpp)->item == i)
{
*lpp = (*lpp)->next;
break;
}
}
please need explain about:
lpp = &(*lpp)->next, can I write it as lpp = lpp->next, is this not the same?
*lpp = (*lpp)->next
the bottom line , I do not see how this function deletes a struct node from the list

lpp points either to the first element of the list or to the next pointer of some element.
By *lpp = (*lpp)->next you are writing it directly into the memory. E.g. consider a list
| el0 | -> | el1 | -> | el2 | -> NULL
list list->next
list from you code points to el0 and lpp = &list.
Now, there are two cases:
el0 matches i: --> list becomes |el0|.next which is el1. After running this function, you have
| el1 | -> | el2 | -> NULL
list list->next
elX matches i (with X>0): lpp is &el_{X-1}.next and by *lpp = ..., this .next will point to elX.next. E.g. assuming el1 matches, you get
| el0 | -> | el2 | -> NULL
lpp = &(*lpp)->next is used to get a reference to next. A simple lpp = lpp->next does not suffice, because it are different types. When you work on lpp->next, a *lpp is like *lpp->next which would dereference the content of the next element.
Single list operations
Although unrelated to this question but due to other discussions, some more code...
Assuming a data structue like
struct node {
int data;
struct node *next;
};
In real code, data would not be a member of this node but struct node would be a mix-in within another object and something like container_of is used to access it. But for this question, keep it as above...
We can define some functions like
void slist_add(struct node *node, struct node *root)
{
node->next = root->next;
root->next = node;
}
void slist_remove(struct node **node)
{
if (node)
*node = (*node)->next;
}
struct node **slist_search(struct node *root, int key)
{
struct node **ptr;
for (ptr = &root->next; *ptr; ptr = &(*ptr)->next) {
if ((*ptr)->data == key)
return ptr;
}
return NULL;
}
Then, we use an empty struct node as an anchor:
int main(void)
{
struct node head = { .next = NULL };
/* add a node */
{
struct node *n = malloc(sizeof *n);
n->data = 23;
slist_add(n, &head);
}
/* add a node */
{
struct node *n = malloc(sizeof *n);
n->data = 42;
slist_add(n, &head);
}
/* verify our expectations... */
assert(head.next != NULL);
assert(head.next->data == 42);
assert(head.next->next != NULL);
assert(head.next->next->data == 23);
assert(head.next->next->next == NULL);
/* remove the node */
{
struct node **ptr = slist_search(&head, 42);
assert(ptr != NULL);
assert(*ptr != NULL);
assert((*ptr)->data == 42);
if (ptr) {
struct node *n = *ptr;
slist_remove(ptr);
free(n);
}
}
/* remove the node */
{
struct node **ptr = slist_search(&head, 23);
assert(ptr != NULL);
assert(*ptr != NULL);
assert((*ptr)->data == 23);
if (ptr) {
struct node *n = *ptr;
slist_remove(ptr);
free(n);
}
}
assert(head.next == NULL);
}

Your code is an extremely simplified and incomplete node delete attempt.
You have to take care of the edge cases as well as actually free the memory.
This line:
*lpp = (*lpp)->next;
is responsible for taking out the node from the list.
It only works if *lpp is the list head and there is another element on the list.
The *lpp points to the node which you do not need anymore and it is replaced by the the next node on the list
(*lpp)->next;
lpp = &(*lpp)->next, can I write it as lpp = lpp->next, is this not
the same?
No it is not. And lpp = lpp->next will not compile.
& is a dereference operator. It is obtaining the address of the node pointer.
You can write this line as
lpp = & ( (*lpp)->next );
and you can recognize (*lpp)->next as the next node pointer on the list.
lpp is a pointer to pointer. *lpp->next is expression known to the compiler but not the lpp->next.
I guess that you misunderstood
lpp = & ( (*lpp)->next );
as
lpp = &* (lpp->next);
and thought that &* would cancel itself out.
If you want to delete the node in the middle of the list you have to connect
the node which exists before the node to be deleted to the node located after the node marked for deletion.
Something similar to:
prev = current;
to_free = current->next; // node to be freed
prev->next = to_free->next; // connect nodes before deletion
free(to_free)
can you please show to me how do I delete a linked list node using
double poniters? – Fela93
I have added the test program for node deletion:
#include <stdio.h>
#include <stdlib.h>
// Basic simple single list implementation to illustrate
// a proper deletion of the node which has a specfic data value.
// Node in List
typedef struct node {
int data;
struct node* next; // pointer to next node
}node;
// returns newly created node
node* node_new(int data)
{
node* new_node = malloc(sizeof(node)); // allocate memory for the node
if (new_node == NULL)
return NULL; // protection
new_node->data = data; // remember the data
new_node->next = NULL; // no next node
return new_node; // return new created node
}
// The method creates a node and prepends it at the beginning of the list.
//
// Frequently used names for this method:
//
// insert at head
// add first
// prepend
//
// returns new head or NULL on failer
node* add_node(node **head, node* new_node)
{
// Add item to the front of the in_list, return pointer to the prepended node (head)
if(head == NULL)
return NULL;
if(new_node == NULL) // problem, not enough memory
return NULL; // in_list->head has not changed
/*
new_node
|*| --> NULL
next
*/
if(*head == NULL) // if list is empty
{
*head = new_node; // the new_node becomes a head
}
else // list already have a head node
{
/*
|2|-->|1|-->NULL
^
|
*
head (2) (list pointer)
*/
new_node->next = *head; // now, the new node next pointer points to the node pointed by the list head, see below:
/*
new_node
|3|--> |2|-->|1|-->NULL
^
|
*
head (list pointer)
*/
*head = new_node; // the list head has to move to new_node ( a new prepanded node)
/*
new_node
|3|--> |2|-->|1|-->NULL
^
|
*
head (3) (list pointer)
*/
}
return *head; // we are returning pinter to new_node
}
// Print out list
void print_nodes(node** head)
{
node* node;
if (head == NULL) {
return;
}
if (*head == NULL){
printf("List is empty!\n");
return;
}
printf("List: ");
node = *head;
while(node != NULL)
{
printf(" %d", node->data);
node = node->next;
}
printf("\n");
}
struct node *find(struct node *start, int data) // find p to be removed
{
node* node;
if (start == NULL)
return NULL;
node = start;
while(node != NULL)
{
if (node->data == data)
return node;
node = node->next;
}
return NULL;
}
int delete(struct node **start, int data)
{
struct node *p, *prev, *next, *to_free;
if (start == NULL) // protection
return 0;
p = find(*start, data); // find element to be removed
if (p == NULL)
return 0;
if (*start == NULL)
return 0; // protection
if(*start == p) // head == p
{
if((*start)->next !=NULL)
{
*start = (*start)->next; // move head
printf("Will be removed: %p\n",p);
free(p); // remove old head
return 1;
}
else // the only node
{
free(p); // free the node pointed by *start (header)
printf("Last node removed\n");
*start = NULL; // header points to NULL
return 1;
}
}
// p != start:
next = *start;
while (next != NULL)
{
prev = next;
to_free = next->next; // candidate to be freed
if( to_free == p )
{
prev->next = to_free->next; // connect nodes before deletion
free(to_free); // now free the remembered `next`
to_free = NULL; // so it does not point to the released memory
return 1;
}
next = next->next; // this node was not a match
} //while
return 0;
}
int main() {
node *head = NULL;
printf("head: %p\n", head);
node *n1 = node_new(1);
node *n2 = node_new(2);
node *n3 = node_new(3);
print_nodes(&head);
add_node(&head, n1);
add_node(&head, n2);
add_node(&head, n3);
printf("head points to: %p\n", head);
// list has 3 elements
print_nodes(&head);
delete(&head, 3);
print_nodes(&head);
delete(&head, 1);
print_nodes(&head);
delete(&head, 2);
print_nodes(&head);
printf("head points to: %p\n", head);
print_nodes(&head);
return 0;
}
Output:
head: (nil)
List is empty!
head points to: 0x5617cd3802b0
List: 3 2 1
Will be removed: 0x5617cd3802b0
List: 2 1
List: 2
Last node removed
List is empty!
head points to: (nil)
List is empty!

Related

why free function causes infinite loop?

I created a program in c which :
Creates a simple linked list in c in which I store letters
Print the content of every node
delete the last node
Print the content of the list again
The problem is with the "delete_last" function because prints in terminal an infinite loop (I believe that the problem is invoked when I use free funtion.)
#include<stdio.h>
#include<stdlib.h>
typedef struct node {
char xar;
struct node *next;
}Node;
void insert_list(Node **head , int len)
{
char x;
Node **list;
Node *node1 , *node2;
node1=(Node*)malloc(sizeof(Node));
printf("Give 5 characters : ");
x=getchar();
node1->xar = x;
node1->next=NULL;
list=&node1;
int i=0;
for(i=1 ; i < len ; i++)
{ x=getchar();
node2 = (node*)malloc(sizeof(node));
node2->xar = x;
node2->next = NULL;
(*list) -> next = node2;
list = &(*list) -> next ;
}
*head=node1;
}
void print_list(Node *head)
{
Node**lpp;
for(lpp=&head ; *lpp!=NULL ; lpp=&(*lpp)->next)
{
printf("\n the chars are %c" , (*lpp)->xar);
}
}
void delete_last(Node *head)
{
Node **lpp;
lpp=&head;
while((*lpp)->next!=NULL)
{
lpp=&(*lpp)->next;
}
free(*lpp);
}
int main()
{
Node *kefali ;
kefali = NULL;
insert_list(&kefali , 5);
print_list(kefali);
printf("\n");
delete_last(kefali);
print_list(kefali);
return 0;
}
You mustn't access to freed objects.
In the delete_last functon, you called free() for one of the nodes, but you didn't update any pointers there. This will have the following call of print_list access a freed object, invoking undefined behavior.
You should add
*lpp = NULL;
after
free(*lpp);
To get the freed node out of the list.
Note that this won't work for removing the first (only) element in the list because the head is passed as a copy. You should change the function to accept a pointer to the head pointer to enable it remove the first element.
Your delete_last lacks a way of telling that the last element was deleted. Either pass a pointer to head or return a new head.
Further, it's way to complicated. Using lpp as pointer to pointer is not necessary - it only complicates the code. Keep it simple.
Here is an example which returns the new head.
Node* delete_last(Node *head)
{
if (head == NULL) return NULL; // empty list
if (head->next == NULL)
{
// Only one element...
free(head);
return NULL;
}
Node *prev = head;
Node *lpp = prev->next;
while (lpp->next)
{
prev = lpp;
lpp = prev->next;
}
prev->next = NULL;
free(lpp);
return head;
}
and call it like:
head = delete_last(head);
Here is an example which takes a pointer to head.
Node* delete_last(Node **head)
{
if (head == NULL) exit(1); // illegal call
if (*head == NULL) return NULL; // empty list
if ((*head)->next == NULL)
{
// Only one element...
free(*head);
*head = NULL;
return;
}
Node *prev = *head;
Node *lpp = prev->next;
while (lpp->next)
{
prev = lpp;
lpp = prev->next;
}
prev->next = NULL;
free(lpp);
}
and call it like:
delete_last(&head);
You do not update the previous node (you need to keep track on it when iterating)
This makes no sense as you take reference to the local variable head and it does not change the the head of list when last element is deleted.
Node **lpp;
lpp=&head;
To prevent double-pointer function returns the head. Assign it when called. If return value is NULL the last element was deleted
Node *delete_last(Node *head)
{
Node *lpp = NULL, *prev;
if(head)
{
lpp=head -> next;
prev = head;
while(lpp->next)
{
prev = lpp;
lpp = lpp -> next;
}
if(prev == head && lpp == NULL)
{
free(head);
head = NULL; //empty list
}
else
{
free(lpp);
prev -> next = NULL;
}
}
free(lpp);
return head;
}
You can also use double pointer to modify the head when needed:
void delete_last(Node **head)
{
Node *lpp = NULL;
if(head && *head)
{
if(!(*head) -> next)
{
free(*head);
*head = NULL;
}
else
{
lpp = *head;
while(lpp -> next -> next)
{
lpp = lpp -> next;
}
free(lpp -> next);
lpp -> next = NULL;
}
}
}

How can I fix this error in c code for linked lists?

I am receiving an error on Xcode when for my delete function for a single linked list in c and at this point im not sure why. The error im receiving is
"malloc: * error for object 0x7ffeefbff5d8: pointer being freed was not allocated * set a breakpoint in malloc_error_break to debug"
int delete (node ** list, int delete)
{
node * current= *list;//declares current
//stop at node that is one before i will need to delete (in this case iam deleting the 2node)
if (*list != NULL)
{
node * temp= *list;
temp = temp->next;
free(list);
*list=temp;
}
while (current->next->data != delete)
{
current=current->next;
}
node * temp;//Declares temp
temp=current->next->next;
free(current->next);//one after current free'd
current->next=temp;
return 0;
}
There are number of things which is not clear or unnecessary
Why you want to free the free(list); list is double pointer, if you free this headpointer of linked list will loose. after delete operation How will print/access the list again if needed ?
list is the headpointer which is holding the link and you didn't allocated any memory for list in main() or calling function so you can't free it. thats why error is
pointer being freed was not allocated
If you want to free only then first you should do free(*list) and then free(list)
From your code segment
free(list); /* list is freed here */
*list=temp; /* How will you access again *list */
As per my thinking your if block should be
if (*list != NULL) {
node * temp= *list;
temp = temp->next; /* temp holds 2nd node address & you need that only */
}
First of all Your question was very unclear, for the next time please post all of the necessary code and explain the purpose of the function in a few line.
I guess what you are trying to do is to remove a specific item from a linked list according to the value of the parameter data.
From the site learn-c:
"
Removing a specific item
To remove a specific item from the list, either by its index from the beginning of the list or by its value, we will need to go over all the items, continuously looking ahead to find out if we've reached the node before the item we wish to remove. This is because we need to change the location to where the previous node points to as well.
Here is the algorithm:
Iterate to the node before the node we wish to delete
Save the node we wish to delete in a temporary pointer
Set the previous node's next pointer to point to the node after the node we wish to delete
Delete the node using the temporary pointer
There are a few edge cases we need to take care of, so make sure you understand the code. "
Then they posted the code, I think you should go there and read the entire post.
Good luck
Your code have a faulty logic plus obvious errors.
Once list is freed you have no right to access it again.
free(list);
*list=temp;
Managing the linked list is not difficult but it requires attention to details.
When you delete the node in the list you have a responsibility to connect the nodes.
If you delete the head you have a responsibility to move the head.
If the head is the node you are looking for and this is the only node in the list than after removal that node has to be marked as NULL.
The test program below uses struct node *find(struct node *start, int data) function to find a node which matches your criteria and uses delete to delete it. All edge cases have been taken care of.
#include <stdio.h>
#include <stdlib.h>
// Basic simple single list implementation to illustrate
// a proper deletion of the node which has a specfic data value.
// Node in List
typedef struct node {
int data;
struct node* next; // pointer to next node
}node;
// List structure
typedef struct list {
node* head; // The entry point into a linked list. If the list is empty then the head is a null reference.
} list;
// Create list
list* list_create()
{
list* new_list = malloc(sizeof(list));
if(new_list == NULL)
return NULL; // protection
new_list->head = NULL; // If the list is empty then the head is a null reference. no elements in the list
return new_list; // return created new list
}
// returns newly created node
node* node_new(int data)
{
node* new_node = malloc(sizeof(node)); // allocate memory for the node
if (new_node == NULL)
return NULL; // protection
new_node->data = data; // remember the data
new_node->next = NULL; // no next node
return new_node; // return new created node
}
// The method creates a node and prepends it at the beginning of the list.
//
// Frequently used names for this method:
//
// insert at head
// add first
// prepend
//
// returns new head or NULL on failer
node* prepend_node(list* in_list, node* new_node)
{
// Add item to the front of the in_list, return pointer to the prepended node (head)
if(in_list == NULL)
return NULL;
if(new_node == NULL) // problem, not enough memory
return NULL; // in_list->head has not changed
/*
new_node
|*| --> NULL
next
*/
if(in_list->head == NULL) // if list is empty
{
in_list->head = new_node; // the new_node becomes a head
}
else // list already have a head node
{
/*
|2|-->|1|-->NULL
^
|
*
head (2) (list pointer)
*/
new_node->next = in_list->head; // now, the new node next pointer points to the node pointed by the list head, see below:
/*
new_node
|3|--> |2|-->|1|-->NULL
^
|
*
head (list pointer)
*/
in_list->head = new_node; // the list head has to move to new_node ( a new prepanded node)
/*
new_node
|3|--> |2|-->|1|-->NULL
^
|
*
head (3) (list pointer)
*/
}
return in_list->head; // we are returning pinter to new_node
}
// Print out list
void print_list(list* in_list)
{
node* node;
if (in_list == NULL)
{
return;
}
if (in_list->head == NULL)
{
printf("List is empty!\n");
return;
}
printf("List: ");
node = in_list->head;
while(node != NULL)
{
printf(" %d", node->data);
node = node->next;
}
printf("\n");
}
struct node *find(struct node *start, int data) // find p to be removed
{
node* node;
if (start == NULL)
return NULL;
node = start;
while(node != NULL)
{
if (node->data == data)
return node;
node = node->next;
}
return NULL;
}
int delete(struct node **start, int data)
{
struct node *p, *prev, *next, *to_free;
if (start == NULL) // protection
return 0;
p = find(*start, data); // find p to be removed
if (p == NULL)
return 0;
if (*start == NULL)
return 0; // protection
if(*start == p) // head == p
{
if((*start)->next !=NULL)
{
*start = (*start)->next; // remember next
free(p);
printf("Head removed\n");
return 1;
}
else // the only node
{
free(p);
printf("Last node removed\n");
*start = NULL;
return 1;
}
}
// p != start:
next = *start;
while (next != NULL)
{
prev = next;
to_free = next->next; // candidate to be freed
if( to_free == p )
{
prev->next = to_free->next; // connect nodes before deletion
free(to_free); // now free the remembered `next`
to_free = NULL; // so it does not point to the released memory
return 1;
}
next = next->next; // this node was not a match
} //while
return 0;
}
int main() {
list* new_list = list_create();
node *n1 = node_new(1);
node *n2 = node_new(2);
node *n3 = node_new(3);
// list is empty
print_list(new_list);
prepend_node(new_list, n1);
prepend_node(new_list, n2);
prepend_node(new_list, n3);
// list has 3 elements
print_list(new_list);
delete(&new_list->head, 3);
print_list(new_list);
delete(&new_list->head, 1);
print_list(new_list);
delete(&new_list->head, 2);
// list has 2 elements
print_list(new_list);
printf("head: %p\n",new_list->head);
print_list(new_list);
free (new_list); // after deleting all elements, delete the list itself
return 0;
}
Output:
List is empty!
List: 3 2 1
Head removed
List: 2 1
List: 2
Last node removed
List is empty!
head: (nil)
List is empty!

Why position 0 in delete node does not work?

Im new to c programming. I wanted to create a linked list from a given file and then randomly get a node from linked list then delete that node.
So the code works great but for the position 0 in linked list does not work.
Please help me
here's the code:
typedef struct node{
int *name;
struct node *next;
}node;
delete node:
void deleteNode(node **head_ref, int position){
if(*head_ref == NULL){
return;
}
node * temp = *head_ref;
if(position == 0)
{
*head_ref = (*head_ref)->next;
return;
}
int h;
for(h=0 ; temp!=NULL && h<position-1 ; h++){
temp = temp->next;
}
if(temp == NULL || temp->next == NULL)
return;
node * next = temp->next->next;
free(temp->next);
temp->next = next;}
getting random node:
void RandomFromList(node *head){
// IF list is empty
if (head == NULL){
return -1;
}
word = head->name;
// Iterate from the (k+1)th element to nth element
node *current = head;
int n;
for (n=2; current!=NULL; n++)
{
// change result with probability 1/n
if (rand() % n == 0)
word = current->name;
// Move to next node
current = current->next;
}
sprintf(words , "%s" , word);
deleteNode(&head , search(head , word));
printf("Randomly selected key is %s\n", words);}
and the file Reader:
node* fileReader(FILE *file){
node *t = malloc(sizeof(node));
char TopicName[20];
int fileRead = fscanf(file,"%s",TopicName);
if(fileRead != EOF){
t->name = strdup(TopicName);
tedad++;
t->next = fileReader(file);
}
if(fileRead == EOF) {
return NULL;
}
return t;}
EDIT:
When the code run's and when the position randomly got 0 the 0 position of linked list doesn't delete and continues with that node in linked list.
EDIT2:I changed my delete node and it works well without any problem, thank you guys!
node* deleteNode(node* head, unsigned i){
node* next;
if(head == NULL)
return head;
next = head->next;
return i == 0
? (free(head), next)
: (head->next = delete_at_index(next, i - 1), head);
}
The major logical problem I see with your delete function is that it is void, i.e. it returns nothing. This is fine if the node being deleted is in the middle (or end) of the list, because the head does not change. But for the case of deleting the head, the caller might expect that his reference would then point to the next node (or null, if a list of one element) after making the call. Consider this code:
node* deleteNode (node *head_ref, int position)
{
// passing in a NULL list returns NULL
if (head_ref == NULL) {
{
return NULL;
}
// deleting the first element returns the second element as the new head
node* temp = head_ref;
if (position == 0)
{
node* ret = temp->next;
free(head_ref);
return ret;
}
// otherwise walk down the list to one before the deletion position
for (int h=0; temp != NULL && h < position-1; h++) {
temp = temp->next;
}
// if found, delete the node at the desired position
if (temp != NULL && temp->next == NULL) {
node* next = temp->next->next;
free(temp->next);
temp->next = next;
}
// for cases other than deleting the head, just return the current
// (unmodified) head
return head_ref;
}
This isn't related to your problem, but don't forget to free the memory:
node * temp = *head_ref;
if(position == 0)
{
*head_ref = temp->next;
free(temp); // <--------
return;
}
Also, you already have a pointer (temp) to *head_ref, it looks cleaner to me to just use that pointer instead of dereferencing head_ref again.
void deleteNode(node **head_ref, int pos){
node *del;
for ( ; *head_ref; head_ref = &(*head_ref)->next) {
if (pos-- <= 0) break;
}
if (!*head_ref) return; // Reached end of list: nothing found
del = *head_ref;
*head_ref = del->next;
free(del);
return;
}
If you want to keep deleteNode void, then the problem is with your RandomFromList function. You are just changing the * head that exists in the function body not the pointer you passed to the function, so it's still pointing to the previous node.
It's because that pointers are passed by value (copied) like other things in C.
Try making RandomFromList return the head pointer.
P.s. I think you also have some memory leaks in the delete function.

Insertion at end in linked list

I am inserting node at the end of the list but my code is printing only the first element and running in infinite loop.
I am unable to figure out the error in my code.
typedef struct nodetype
{
int info;
struct nodetype* next;
}node;
node *head=NULL;
void insertatend(int x);//x is the key element.
void print();
void insertatend(int x)
{
node *ptr;
ptr=(node*)malloc(sizeof(node));
ptr->info=x;
if(head==NULL)
{
ptr->next=ptr;
head=ptr;
}
else
ptr->next=ptr;
}
void print() //To print the list
{
node *temp=head;
printf("List is-");
while(temp!=NULL)
{
printf("%d",temp->info);
temp=temp->next;
}
}
Consider your insert method (I will take head as a parameter here instead of a global)
void insertatend(node **hd, int x) {
node *ptr = NULL, *cur = NULL;
if (!(ptr = malloc(sizeof (node)))) {
return;
}
if (!*hd) {
*hd = ptr;
} else {
cur = *hd;
while (cur->next) {
cur = cur->next;
}
cur->next = ptr;
}
}
You need to traverse your list from the end to its back in order to perform the insertion correctly. (Hence the while loop in the above function).
Your "temp != NULL" will never become false after the insertion, because in that insertion you set the next pointer to itself, thus creating a link loop.
it should be more like this:
void insertatend(int x)
{
node *ptr;
ptr=malloc(sizeof(node)); //don't cast pointers returned by malloc
ptr->info=x;
ptr->next=NULL; //set next node pointer to NULL to signify the end
if(head==NULL)
{
head=ptr;
}
else
{
node* tmp = head;
while(tmp->next) tmp = tmp->next; //get last node
tmp->next=ptr; //attach new node to last node
}
}
also your else branch was incorrect, creating another link loop.
You need to pass the last element of the list:
void insertatend(node *last, int x)
Or put a a tail node as global:
node *head = NULL;
node *tail = NULL;
void insertatend(int x)
{
node *ptr;
ptr = malloc(sizeof(node)); /* Don't cast malloc */
ptr->info = x;
ptr->next = NULL;
if (head == NULL) {
head = ptr;
} else {
tail->next = ptr;
}
tail = ptr;
}
You could also redefine your node struct to include next, prev, head, and tail pointers and manipulate them appropriately.
In your case, you should only need to set the head pointer on the tail node and the tail pointer on the head node. Set next and prev on all nodes. head pointer on head node should point to itself; tail pointer on tail node should point to itself. Head->prev = NULL; Tail->next = NULL;
Then just pass the head pointer always to your insertatend func.

inserting in singly linked list in ascending order

Say I have a singly linked list of elements in ascending order that looks like:
A->B->D->E
I want to insert C in between B and D.
I know how to point C to D, but I don't know how to point B to C since the linked list does not keep track of the prev node.
While you are scanning down the list of nodes you have to keep two pointers: one points to the current node that you are interested in, and the other points to the previous node.
A possible implementation follows:
struct node {
int value;
struct node *next;
};
void sorted_insert(struct node **head, struct node *element) {
// Is the linked list empty?
if (*head == NULL) {
element->next = NULL;
*head = element;
return;
}
// Should we insert at the head of the linked list
if (element->value < (*head)->value) {
element->next = *head;
*head = element;
return;
}
// Otherwise, find the last element that is smaller than this node
struct node *needle = *head;
while (true) {
if (needle->next == NULL)
break;
if (element->value < needle->next->value)
break;
needle = needle->next;
}
// Insert the element
element->next = needle->next;
needle->next = element;
return;
}
You don't need to point B to C, or to maintain a pointer to the previous element. One method is:
Step to node D
malloc() a new node
Copy the data and next member from node D to your new node
Copy the data for node C into the existing node D (which now becomes node C)
Point the next member of the old node D to the new node.
For instance, excluding the possibility of inserting at the head of the list:
void insert(struct node * head, const int data, const size_t before)
{
assert(before > 0);
struct node * node = head;
while ( before-- && node ) {
node = node->next;
}
if ( !node ) {
fprintf(stderr, "index out of range\n");
exit(EXIT_FAILURE);
}
struct node * new_node = malloc(sizeof *new_node);
if ( !new_node ) {
perror("couldn't allocate memory for node");
exit(EXIT_FAILURE);
}
new_node->data = node->data;
new_node->next = node->next;
node->next = new_node;
node->data = data;
}
Why not keep track of the 'previous' node as you iterate through the list? Please forgive any syntactic shortcomings, as I haven't compiled this, but this should give you the idea.
struct node {
char name[ 10 ];
struct node *next;
};
struct list {
struct node *head;
};
void insert_C(struct list *list) {
struct node *new_node = malloc(sizeof(struct node));
if( new_node == NULL ) {
/* Error handling */
}
strcpy(new_node->name, "C");
struct node *pnode;
struct node *prev_node = NULL;
for( pnode = list->head; pnode->next != null; pnode = pnode->next ) {
if( !strcmp(pnode->name, "D") ) {
if( prev_node == NULL ) {
/* The 'C' node is going to be the new head. */
new_node->next = list->head;
list->head = new_node;
}
else {
prev_node->next = new_node;
new_node->next = pnode;
}
break;
}
/* Remember this node for the next loop iteration! */
prev_node = pnode;
}
}

Resources