Delete last node of single linked list using tail pointer - c

I'm creating a singly linked list in C which has head and tail pointers where the head pointer points to starting node of SLL and tail pointer points to the last node of SLL. I don't want to traverse till the end of the list using the head pointer to delete the node. Is there a way so that I can use the tail pointer to delete the last element of SLL?
Following is node addition function. head and tail are initiated NULL.
void add_node_last(Node** head, Node** tail, int data) {
Node* new_node = (Node *) malloc(sizeof(Node));
new_node -> data = data;
new_node -> ptr = NULL;
if(*head == NULL && *tail == NULL) {
*head = new_node;
*tail = new_node;
return;
}
(*tail) -> ptr = new_node;
*tail = new_node;
}
To delete the first node, the following function is used:
void del_first(Node **head) {
if(*head == NULL) {
return;
}
*head = (*head) -> ptr;
free(*head);
}

You can free the node's memory, but only once. After the first removal, your tail pointer, and the ptr of the second to last node, will be invalid pointers. In order to make sure both pointers are always correct you either need to traverse the entire list or make it a doubly linked list.
That's a long way of saying no (thanks to #stark).

in order for this to work without making it a doubly linked list, you could have the tail's *next pointer point to the head of the list, then traverse through it till you reach the node before the tail. once there you can NULL its *next pointer which would essentially detach the original tail from the list. you would then set the tail to the current node, then finally free the original tail.
void del_last(Node **head, Node **tail) {
struct node *new_head = *head;
struct node *current = *tail;
current->next = head;
while(current-> != *tail) //getting the node right before the original tail
{
current = current->next
}
current->next = NULL; //detaches original tail form list
free(**tail); //gets rid of original tail
**tail = current; // sets current node to tail pointer
}

Related

Explanation for base case return value in recursive linked list reverse algorithm

I am learning how to reverse a linked list recursively. I am confused with the last 4 lines.
node *reverse_linked_list_rec(node *head){
if (head->next==NULL){
return head;
}
node *smallans= reverse_linked_list_rec(head->next);
node *tail = head->next;
tail->next = head;
head->next = NULL;
return smallans;
}
Let's say I am reversing
1 2 3 NULL
by recursion, it reaches at 3 NULL and then by base case returns
2 3 NULL
here head=2, smallans=2 (not sure).
Why we are returning smallAns here and how it is changing?
smallans is a confusing variable name because it's actually the old tail being passed back through the list to become the new head which is ultimately returned to the caller.
Its next pointer changes when these lines execute in the parent function call:
// when head->next->next == NULL ...
node *tail = head->next; // ... `tail` points to the old tail (new head) ...
tail->next = head; // ... and this sets the new tail's next pointer to
// the old second-to-last node (new second node).
tail is a misleading name here--I associate a "tail" with a single node that terminates the entire list, not a previous node. new_prev or old_next seem more appropriate here depending on whether you want to name things relative to the node roles in the new list or the original list.
As a minor point, I recommend using if (!head || !head->next) to avoid a potential null pointer dereference.
I'd write the function as follows:
node *reverse_linked_list_rec(node *head) {
if (!head || !head->next) {
return head;
}
node *old_tail = reverse_linked_list_rec(head->next);
node *old_next = head->next;
old_next->next = head;
head->next = NULL;
return old_tail;
}
Aside from intellectual curiosity, recursion is a poor choice for linked list operations since it adds function call overhead, you can blow the stack and the logic isn't any easier to follow than iterative, in most cases.
Case in point, here's a complete example with an iterative version:
#include <stdio.h>
#include <stdlib.h>
struct node {
int id;
struct node *next;
};
struct node *make_node(int id) {
struct node *n = malloc(sizeof(*n));
if (!n) exit(1);
n->id = id;
n->next = NULL;
return n;
}
struct node *reverse_linked_list(struct node *head) {
struct node *prev = NULL;
for (struct node *curr = head; curr;) {
struct node *old_next = curr->next;
curr->next = prev;
prev = curr;
curr = old_next;
}
return prev;
}
void print_linked_list(struct node *head) {
for (; head; head = head->next) {
printf("%d->", head->id);
}
puts("");
}
void free_linked_list(struct node *head) {
while (head) {
struct node *tmp = head;
head = head->next;
free(tmp);
}
}
int main() {
struct node *head = make_node(1);
head->next = make_node(2);
head->next->next = make_node(3);
print_linked_list(head); // => 1->2->3->
head = reverse_linked_list(head);
print_linked_list(head); // => 3->2->1->
free_linked_list(head);
return 0;
}
As another minor point, since the linked list is being mutated I'd probably go for a header like void reverse_linked_list(struct node **head);. Otherwise, it seems too easy to call the non-void function, ignore the return value and wind up with a memory leak or crash when head in the caller scope (which has become a tail pointing to null) is dereferenced.

how to insert node in Circular Linked list in C

Can someone explain the 'while part' of the code?- in the main() head is declared as struct node pointer initialized with value NULL. and a node is inserted by calling function = push(&head,12)
void push(struct Node **head_ref, int data)
{
struct Node *ptr1 = (struct Node *)malloc(sizeof(struct Node));
struct Node *temp = *head_ref;
ptr1->data = data;
ptr1->next = *head_ref;
if (*head_ref != NULL)
{
while (temp->next != *head_ref)
temp = temp->next;
temp->next = ptr1;
}
else
ptr1->next = ptr1; /*For the first node */
*head_ref = ptr1;
}
Can someone explain the 'while part' of the code?
The while() part (also described in section 2.) below.) is to look for the node which points to the current head node so that the new node can be inserted just before it. While in the loop, notice that as nodes are found, and they are not yet the head node, they are incremented so as to prepare for the insertion when head node is finally located.
The last expression in the routine:
*head_ref = ptr1;
Is to handle the case described by section 1.) in the general steps below.
General approach for the following initial conditions:
Linked List is empty:
a) since new_node is the only node in CLL, make a self loop.
new_node->next = new_node;
b) change the head pointer to point to new node.
*head_ref = new_node;
New node is to be inserted just before the head node:
(a) Find out the last node using a loop.
while(current->next != *head_ref)
current = current->next;
(b) Change the next of last node.
current->next = new_node;
(c) Change next of new node to point to head.
new_node->next = *head_ref;
(d) change the head pointer to point to new node.
*head_ref = new_node;
New node is to be inserted somewhere after the head:
(a) Locate the node after which new node is to be inserted.
while ( current->next!= *head_ref &&
current->next->data data)
{ current = current->next; }
(b) Make next of new_node as next of the located pointer
new_node->next = current->next;
(c) Change the next of the located pointer
current->next = new_node;
Details and implementation for doing this are shown in this example code

Why does my code not insert a new node into this linked list?

I am new to programming. I just want to know why this doesn't work.
My understanding of pointers isn't clear, especially when using pointers across functions.
void append(struct Node** head_ref, float new_data)
{
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
new_node->data = new_data;
new_node->next = NULL;
while (last != NULL)
last = last->next;
last = new_node;
return;
}
void append(struct Node** head_ref, float new_data)
{
while (last->next != NULL)
last = last->next;
last->next = new_node;
return;
}
In the first function the new data doesn't get included, I get only the original linked list.
But the second function works just fine. How does a double pointer work when inserting a new node in the beginning of the linked list? (I have seen answers regarding this question, but I am still confused)
In the first example, you move the pointer last until it points at a NULL location. Then, you set the pointer to new_node. However, at this point, last has no real association to your linked list. It is just a pointer to some memory. In the second example, the correct one, you iterate until you reach the tail of the linked list, where next of that node is NULL. Then, you set that next to new_node. There is now a new tail to the list, that is new_node.
Changing the local variable last does not change the value of the data member next of the previous (last) node.
To be more clear let's assume that the list is empty. Then you have to change the pointer referenced by this double pointer head_ref.
You declared a new pointer
struct Node *last = *head_ref;
The loop
while (last != NULL)
last = last->next;
is skipped because now already last is equal to NULL die to the initialization in the previous declaration. And then you changed this local variable last
last = new_node;
The original value of the pointer pointed by head_ref was not changed because last and *head_ref occupy different extents of memory. You changed the memory occupied by last but not changed the memory occupied by head_ref.
Also you should check whether a memory was successfully allocated.
The function can look the following way
int append( struct Node **head_ref, float new_data )
{
struct Node *new_node = malloc( sizeof( struct Node ) );
int success = new_node != NULL;
if ( success )
{
new_node->data = new_data;
new_node->next = NULL;
while ( *head_ref != NULL ) head_ref = &( *head_ref )->next;
*head_ref = new_node;
}
return success;
}
As for this loop (I think you wanted just to show the loop not a whole function)
while (last->next != NULL)
last = last->next;
last->next = new_node;
then you are changing the data member next of the previous (last ) node.
Though this loop will not work if initially head_ref is equal to NULL.

Adding nodes to end of a linked list in C

The code I have is adding nodes to head but I want them added to tail. I tried pointing head to next node to null but it stops since the next node is null
if(head==NULL)
{
head=node;
}
else
{
node->next=head;
node->prev=NULL;
head->prev=node;
head=node;
}
printf("The node is inserted to linked list succesfully. \n\n");
printMenu();
You need to keep a pointer to your list's tail, and then adding an element may look like:
node -> next = NULL;
tail -> next = node;
node -> prev = tail;
tail = node;
You need to go to the end of the list first:
if(head==NULL)
{
head=node;
}
else
{
struct nodetype *p = head;
while (p->next)
p = p->next;
p->next = node;
node->prev = p;
}
// Create new node
struct nodeType *newNode = (struct nodeType *)malloc(sizeof(struct nodeType));
// Singly-linked list and new node is end of the list so the next pointer is NULL
newNode->next = NULL;
if(head==NULL){
// If head is not assigned
head = newNode;
}
else{
// Assign the next pointer in the current node to the new node
current->next = newNode;
}
// Update the current pointer to point to the newNode
current = newNode;
where head and current is,
struct nodeType *head, *current;
If the current pointer does not point to the end of the list you can iterate over the list to the end with the following line and then start appending to the linked-list:
for(current = head; current->next != NULL; current = current->next);

Adding a node into a linked list, right after the head

I have a linked list, and I need to create a node right after the head..
it means I have something like this:
node *head = NULL;
and my linked list in the end should be like :
head -> node -> NULL...
but when I use a normal addNode function, it gives me a runtime error(not sure which, my debug has problems)...
this is what I wrote:
void addNode(node *head)
{
node *temp = head; // a temp to not move the head
node *newNode = (node*)malloc(sizeof(node)); // The new node
while (temp -> next != NULL)
{
temp = temp -> next // Getting to the last node
}
temp -> next= newNode; // Adding the new node into the linked list insted of the NULL
newNode -> next = NULL; // Adding the NULL after the new node
}
This code works great to me when I have a linked list with already 1 or more nodes ,but if the linked list only has a head, it does problems to me... how can I solve the problem?
(if you didnt understand my problem - With the addNode function I wrote here, i'm getting a runtime error for adding a new node into a head that points to NULL already)..
Thanks, Amit :)
Need to check if head is NULL on entry, otherwise a null pointer will be dereferenced:
node *temp = head; /* temp set to head, possibly null. */
while (temp->next != NULL) /* 'temp' dereferenced, undefined behaviour
if 'temp' is null. */
In order for the change to be seen by the caller, you will need to pass in a node** (as suggested by wildplasser), as C passes arguments by value. Change to (for example):
void addNode(node **head)
{
node *newNode = malloc(sizeof(node)); /* No need to cast. */
if (newNode)
{
newNode->next = NULL;
if (NULL == *head)
{
*head = newNode; /* Set the head, as there isn't one yet. */
}
else
{
node* temp = *head;
while (temp->next) temp = temp->next;
temp->next = newNode;
}
}
}
This would be called:
node* my_list = NULL;
addNode(&my_list);
You have to check if Head is null. Otherwise when you try to check
head->next != NULL
head is NULL so you are referring to random place in memory
You can't add node after head if head is NULL. You have to allocate memory for head and then set 'next' pointer. By the way why u want to set head->next while head is null?
EDIT
Mayby you should try add flag to nodes like bool active and set it false when you want to pass it.
I'll try to say it in another way. You can't set head->next because head is NULL. NULL means, that it's just a pointer, to nowhere. It's a variable where u can place some address, but nothing else. If u want to have there a structure, like node, you have to place there address of new object of type Node:
Node element = malloc(sizeof(Node));
head = element;
After that u will have in head address of Node object and u will be able to revoke to variables (like Node *next) inside this structure.
You could use a pointer to pointer:
void addNode(node **pp)
{
node *newNode = malloc(sizeof *newNode); // The new node
if (newNode) {
newNode->next = *pp; // steal the parent. (this will also work if *pp happens to be NULL)
*pp = newNode; // let *pp point to the new node
}
}
To be called like:
...
node *head = NULL;
addNode( &head);
...

Resources