🔗 Linked List
Learn how nodes are connected using pointers, visualize pointer changes step by step, and understand how linked-list operations are implemented in C.
📖 Introduction to Linked List
A linked list is a linear data structure made of separate memory blocks called nodes. Unlike an array, the nodes do not need to be stored in consecutive memory locations.
Each node stores the actual data and a pointer that tells us where the next node is located. The first node is reached using a pointer called head.
💡 Key Idea
In an array, elements are connected by index positions. In a linked list, nodes are connected by addresses stored in pointers.
🧠 Simple Example
If the list stores 10, 20, 30, the logical connection is:
head → 10 → 20 → 30 → NULL.
The final NULL tells us that the list has ended.
Why do we use Linked Lists?
- Nodes can be created dynamically when needed.
- Insertion and deletion do not require shifting all later elements.
- The structure can grow or shrink during program execution.
- Linked lists are used to build stacks, queues, graph adjacency lists and many other structures.
a[5] is not available. To reach a node, we usually move from the head one link at a time.
🧩 Node Structure in C
Every singly linked-list node contains data and a pointer to the next node.
C Structure
struct Node
{
int data;
struct Node *next;
};
Understanding each line
struct Nodedefines the format of one node.int data;stores the actual value.struct Node *next;stores the address of another node of the same type.
next does not store the next value. It stores the
address of the next node.
➡️ Singly Linked List
Each node has one link: a pointer to the next node.
💡 Direction of movement
A singly linked list can naturally be traversed only in the forward direction: head → first node → second node → ... → NULL.
🚶 Create and Traverse a Singly Linked List
Create nodes dynamically, connect them, and visit every node from head to NULL.
💡 Traversal Idea
Use a temporary pointer, usually called temp. Start it at head.
Read the current node, then move using temp = temp->next.
Stop when temp == NULL.
Algorithm
1. Set temp = head
2. While temp != NULL
a. Process temp->data
b. Move temp = temp->next
3. Stop when temp becomes NULL
💻 C Program — Create and Traverse
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *next;
};
int main()
{
int n, value;
struct Node *head = NULL;
struct Node *tail = NULL;
struct Node *newNode;
struct Node *temp;
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
scanf("%d", &value);
newNode = (struct Node *)malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = NULL;
if(head == NULL)
{
head = newNode;
tail = newNode;
}
else
{
tail->next = newNode;
tail = newNode;
}
}
temp = head;
while(temp != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
return 0;
}
Sample Input
4
10 20 30 40
Sample Output
10 20 30 40
💻 Program
🧠 What is happening?
📊 Live Variables
🔗 Live Linked List
⚡ Complexity
➕ Insert a Node at the Beginning
Create a new node, point it to the current head, then make it the new head.
🧠 Example
Before insertion: head → 10 → 20 → 30 → NULL
Insert 5 at the beginning.
After insertion: head → 5 → 10 → 20 → 30 → NULL
Algorithm
1. Create newNode
2. Store the new value in newNode->data
3. Set newNode->next = head
4. Set head = newNode
💻 C Program — Insert at Beginning
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *next;
};
struct Node *insertBeginning(struct Node *head, int value)
{
struct Node *newNode;
newNode = (struct Node *)malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = head;
head = newNode;
return head;
}
int main()
{
struct Node *first;
struct Node *second;
struct Node *third;
struct Node *head;
first = (struct Node *)malloc(sizeof(struct Node));
second = (struct Node *)malloc(sizeof(struct Node));
third = (struct Node *)malloc(sizeof(struct Node));
first->data = 10;
second->data = 20;
third->data = 30;
first->next = second;
second->next = third;
third->next = NULL;
head = first;
head = insertBeginning(head, 5);
while(head != NULL)
{
printf("%d ", head->data);
head = head->next;
}
return 0;
}
Initial List
10 20 30
Output After Inserting 5
5 10 20 30
💻 Program
🧠 What is happening?
📊 Live Variables
🔗 Live Linked List
⚡ Complexity
newNode->next and head.➕ Insert a Node at the End
Move to the last node, connect the new node after it, and keep the new node pointing to NULL.
🧠 Example
Before insertion: head → 10 → 20 → 30 → NULL
Insert 40 at the end.
After insertion: head → 10 → 20 → 30 → 40 → NULL
💡 Key Idea
If the list is not empty, use a temporary pointer and move until
temp->next == NULL. At that moment, temp points to the last node.
Then connect temp->next = newNode.
Algorithm
1. Create newNode
2. Store value in newNode->data
3. Set newNode->next = NULL
4. If head == NULL
head = newNode
stop
5. Set temp = head
6. While temp->next != NULL
temp = temp->next
7. Set temp->next = newNode
💻 C Program — Insert at End
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *next;
};
struct Node *insertEnd(struct Node *head, int value)
{
struct Node *newNode;
struct Node *temp;
newNode = (struct Node *)malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = NULL;
if(head == NULL)
return newNode;
temp = head;
while(temp->next != NULL)
temp = temp->next;
temp->next = newNode;
return head;
}
int main()
{
struct Node *first;
struct Node *second;
struct Node *third;
struct Node *head;
struct Node *temp;
first = (struct Node *)malloc(sizeof(struct Node));
second = (struct Node *)malloc(sizeof(struct Node));
third = (struct Node *)malloc(sizeof(struct Node));
first->data = 10;
second->data = 20;
third->data = 30;
first->next = second;
second->next = third;
third->next = NULL;
head = first;
head = insertEnd(head, 40);
temp = head;
while(temp != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
return 0;
}
Initial List
10 20 30
Output After Inserting 40
10 20 30 40
💻 Program
🧠 What is happening?
📊 Live Variables
🔗 Live Linked List
⚡ Complexity
tail pointer can reduce insertion time from O(n) to O(1).
📍 Insert a Node at a Given Position
Move to the node before the required position, then reconnect two links in the correct order.
🧠 Example
Before insertion: head → 10 → 20 → 30 → 40 → NULL
Insert 25 at position 3.
After insertion: head → 10 → 20 → 25 → 30 → 40 → NULL
💡 Why pointer-update order matters
First save the remaining list using newNode->next = temp->next.
Only after that should we execute temp->next = newNode.
If the order is wrong, we may lose access to the rest of the list.
Algorithm
1. Create newNode
2. Store the new value
3. If position == 1
newNode->next = head
head = newNode
stop
4. Set temp = head
5. Move temp to the node at position - 1
6. If temp == NULL
position is invalid
7. Set newNode->next = temp->next
8. Set temp->next = newNode
💻 C Program — Insert at Position
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *next;
};
struct Node *insertAtPosition(struct Node *head, int value, int position)
{
struct Node *newNode;
struct Node *temp;
if(position < 1)
return head;
newNode = (struct Node *)malloc(sizeof(struct Node));
newNode->data = value;
if(position == 1)
{
newNode->next = head;
return newNode;
}
temp = head;
for(int i = 1; i < position - 1 && temp != NULL; i++)
temp = temp->next;
if(temp == NULL)
{
free(newNode);
return head;
}
newNode->next = temp->next;
temp->next = newNode;
return head;
}
int main()
{
struct Node *first;
struct Node *second;
struct Node *third;
struct Node *fourth;
struct Node *head;
struct Node *temp;
first = (struct Node *)malloc(sizeof(struct Node));
second = (struct Node *)malloc(sizeof(struct Node));
third = (struct Node *)malloc(sizeof(struct Node));
fourth = (struct Node *)malloc(sizeof(struct Node));
first->data = 10;
second->data = 20;
third->data = 30;
fourth->data = 40;
first->next = second;
second->next = third;
third->next = fourth;
fourth->next = NULL;
head = first;
head = insertAtPosition(head, 25, 3);
temp = head;
while(temp != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
return 0;
}
Initial List
10 20 30 40
Output After Inserting 25 at Position 3
10 20 25 30 40
💻 Program
🧠 What is happening?
📊 Live Variables
🔗 Live Linked List
⚡ Complexity
p, stop at node p - 1.
Then perform newNode->next = temp->next before temp->next = newNode.
🗑️ Delete a Node at the Beginning
Move head to the second node, then free the old first node.
🧠 Example
Before deletion: head → 10 → 20 → 30 → NULL
Delete the first node.
After deletion: head → 20 → 30 → NULL
💡 Key Idea
Save the current head in a temporary pointer. Move head to
head->next, then release the old first node using free(temp).
Algorithm
1. If head == NULL
stop
2. Set temp = head
3. Set head = head->next
4. free(temp)
5. Return head
💻 C Program — Delete at Beginning
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *next;
};
struct Node *deleteBeginning(struct Node *head)
{
struct Node *temp;
if(head == NULL)
return NULL;
temp = head;
head = head->next;
free(temp);
return head;
}
int main()
{
struct Node *first;
struct Node *second;
struct Node *third;
struct Node *head;
struct Node *temp;
first = (struct Node *)malloc(sizeof(struct Node));
second = (struct Node *)malloc(sizeof(struct Node));
third = (struct Node *)malloc(sizeof(struct Node));
first->data = 10;
second->data = 20;
third->data = 30;
first->next = second;
second->next = third;
third->next = NULL;
head = first;
head = deleteBeginning(head);
temp = head;
while(temp != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
return 0;
}
Initial List
10 20 30
Output
20 30
💻 Program
🧠 What is happening?
📊 Live Variables
🔗 Live Linked List
⚡ Complexity
free().free(head) before saving the next node.
After memory is freed, reading head->next is invalid.
🗑️ Delete a Node at the End
Move to the last node while keeping track of the previous node, then disconnect and free the last node.
🧠 Example
Before deletion: head → 10 → 20 → 30 → 40 → NULL
Delete the last node.
After deletion: head → 10 → 20 → 30 → NULL
💡 Key Idea
A singly linked list cannot move backward. Therefore, while moving toward the last node,
keep another pointer called prev one node behind temp.
Algorithm
1. If head == NULL
stop
2. If head->next == NULL
free(head)
head = NULL
stop
3. Set prev = NULL
4. Set temp = head
5. While temp->next != NULL
prev = temp
temp = temp->next
6. Set prev->next = NULL
7. free(temp)
8. Return head
💻 C Program — Delete at End
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *next;
};
struct Node *deleteEnd(struct Node *head)
{
struct Node *temp;
struct Node *prev;
if(head == NULL)
return NULL;
if(head->next == NULL)
{
free(head);
return NULL;
}
prev = NULL;
temp = head;
while(temp->next != NULL)
{
prev = temp;
temp = temp->next;
}
prev->next = NULL;
free(temp);
return head;
}
int main()
{
struct Node *first;
struct Node *second;
struct Node *third;
struct Node *fourth;
struct Node *head;
struct Node *temp;
first = (struct Node *)malloc(sizeof(struct Node));
second = (struct Node *)malloc(sizeof(struct Node));
third = (struct Node *)malloc(sizeof(struct Node));
fourth = (struct Node *)malloc(sizeof(struct Node));
first->data = 10;
second->data = 20;
third->data = 30;
fourth->data = 40;
first->next = second;
second->next = third;
third->next = fourth;
fourth->next = NULL;
head = first;
head = deleteEnd(head);
temp = head;
while(temp != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
return 0;
}
Initial List
10 20 30 40
Output
10 20 30
💻 Program
🧠 What is happening?
📊 Live Variables
🔗 Live Linked List
⚡ Complexity
temp and prev are used.O(n) time because we need the node before tail.
📍 Delete a Node at a Given Position
Reach the node before the target position, bypass the target node, and then free it.
🧠 Example
Before deletion: head → 10 → 20 → 30 → 40 → NULL
Delete position 3.
After deletion: head → 10 → 20 → 40 → NULL
💡 Key Idea
For position p, stop at node p - 1.
Save the target node in deleteNode, bypass it using
temp->next = deleteNode->next, then call free(deleteNode).
Algorithm
1. If head == NULL or position < 1
stop
2. If position == 1
delete the beginning node
stop
3. Set temp = head
4. Move temp to position - 1
5. If temp == NULL or temp->next == NULL
position is invalid
6. Set deleteNode = temp->next
7. Set temp->next = deleteNode->next
8. free(deleteNode)
9. Return head
💻 C Program — Delete at Position
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *next;
};
struct Node *deleteAtPosition(struct Node *head, int position)
{
struct Node *temp;
struct Node *deleteNode;
if(head == NULL || position < 1)
return head;
if(position == 1)
{
temp = head;
head = head->next;
free(temp);
return head;
}
temp = head;
for(int i = 1; i < position - 1 && temp != NULL; i++)
temp = temp->next;
if(temp == NULL || temp->next == NULL)
return head;
deleteNode = temp->next;
temp->next = deleteNode->next;
free(deleteNode);
return head;
}
int main()
{
struct Node *first;
struct Node *second;
struct Node *third;
struct Node *fourth;
struct Node *head;
struct Node *temp;
first = (struct Node *)malloc(sizeof(struct Node));
second = (struct Node *)malloc(sizeof(struct Node));
third = (struct Node *)malloc(sizeof(struct Node));
fourth = (struct Node *)malloc(sizeof(struct Node));
first->data = 10;
second->data = 20;
third->data = 30;
fourth->data = 40;
first->next = second;
second->next = third;
third->next = fourth;
fourth->next = NULL;
head = first;
head = deleteAtPosition(head, 3);
temp = head;
while(temp != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
return 0;
}
Initial List
10 20 30 40
Output After Deleting Position 3
10 20 40
💻 Program
🧠 What is happening?
📊 Live Variables
🔗 Live Linked List
⚡ Complexity
🔍 Search in a Singly Linked List
Traverse node by node from head until the target value is found or the list ends.
🧠 Example
List: head → 10 → 20 → 30 → 40 → NULL
Target: 30
Result: 30 is found at position 3.
💡 Key Idea
A singly linked list does not support direct indexing like an array.
To search for a value, start from head and compare each node's data one by one.
Algorithm
1. Set temp = head
2. Set position = 1
3. While temp != NULL
a. If temp->data == target
return position
b. temp = temp->next
c. position++
4. Return -1
💻 C Program — Search in Linked List
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *next;
};
int search(struct Node *head, int target)
{
struct Node *temp = head;
int position = 1;
while(temp != NULL)
{
if(temp->data == target)
return position;
temp = temp->next;
position++;
}
return -1;
}
int main()
{
struct Node *first;
struct Node *second;
struct Node *third;
struct Node *fourth;
struct Node *head;
int result;
first = (struct Node *)malloc(sizeof(struct Node));
second = (struct Node *)malloc(sizeof(struct Node));
third = (struct Node *)malloc(sizeof(struct Node));
fourth = (struct Node *)malloc(sizeof(struct Node));
first->data = 10;
second->data = 20;
third->data = 30;
fourth->data = 40;
first->next = second;
second->next = third;
third->next = fourth;
fourth->next = NULL;
head = first;
result = search(head, 30);
if(result == -1)
printf("Not Found");
else
printf("Found at position %d", result);
return 0;
}
Target
30
Output
Found at position 3
💻 Program
🧠 What is happening?
📊 Live Variables
🔗 Live Linked List
⚡ Complexity
🔁 Reverse a Singly Linked List
Reverse every next pointer so the last node becomes the new head.
🧠 Example
Before reversal: head → 10 → 20 → 30 → 40 → NULL
After reversal: head → 40 → 30 → 20 → 10 → NULL
💡 Three-Pointer Technique
Use three pointers:
prev, current, and nextNode.
Before reversing a link, save the next node first. Otherwise, the remaining part of the list would be lost.
Algorithm
1. Set prev = NULL
2. Set current = head
3. While current != NULL
a. nextNode = current->next
b. current->next = prev
c. prev = current
d. current = nextNode
4. Set head = prev
5. Return head
💻 C Program — Reverse Linked List
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *next;
};
struct Node *reverseList(struct Node *head)
{
struct Node *prev = NULL;
struct Node *current = head;
struct Node *nextNode;
while(current != NULL)
{
nextNode = current->next;
current->next = prev;
prev = current;
current = nextNode;
}
head = prev;
return head;
}
int main()
{
struct Node *first;
struct Node *second;
struct Node *third;
struct Node *fourth;
struct Node *head;
struct Node *temp;
first = (struct Node *)malloc(sizeof(struct Node));
second = (struct Node *)malloc(sizeof(struct Node));
third = (struct Node *)malloc(sizeof(struct Node));
fourth = (struct Node *)malloc(sizeof(struct Node));
first->data = 10;
second->data = 20;
third->data = 30;
fourth->data = 40;
first->next = second;
second->next = third;
third->next = fourth;
fourth->next = NULL;
head = first;
head = reverseList(head);
temp = head;
while(temp != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
return 0;
}
Original List
10 20 30 40
Reversed Output
40 30 20 10
💻 Program
🧠 What is happening?
📊 Live Variables
🔗 Live Pointer State
⚡ Complexity
current->next before saving the original next node.
Always execute nextNode = current->next first.
↔️ Doubly Linked List
Each node stores links to both the previous node and the next node.
A doubly linked list (DLL) is a linked list in which every node contains three parts: a pointer to the previous node, the data, and a pointer to the next node.
Node Structure in C
struct Node
{
int data;
struct Node *prev;
struct Node *next;
};
⬅️ Previous Link
prev stores the address of the previous node.
📦 Data
data stores the actual value.
➡️ Next Link
next stores the address of the next node.
💡 Main Advantage
Unlike a singly linked list, a doubly linked list supports movement in both forward and backward directions.
prev),
so a doubly linked list uses more memory than a singly linked list.
🚶 Forward and Backward Traversal
Move using next from head to tail or use prev from tail to head.
🧠 Example
Forward: 10 → 20 → 30 → 40
Backward: 40 → 30 → 20 → 10
Algorithms
Forward Traversal
temp = head
while(temp != NULL)
{
visit(temp->data)
temp = temp->next
}
Reach Tail
temp = head
while(temp->next != NULL)
temp = temp->next
Backward Traversal
while(temp != NULL)
{
visit(temp->data)
temp = temp->prev
}
💻 C Program — Forward and Backward Traversal
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *prev;
struct Node *next;
};
int main()
{
struct Node *first;
struct Node *second;
struct Node *third;
struct Node *fourth;
struct Node *head;
struct Node *temp;
first = (struct Node *)malloc(sizeof(struct Node));
second = (struct Node *)malloc(sizeof(struct Node));
third = (struct Node *)malloc(sizeof(struct Node));
fourth = (struct Node *)malloc(sizeof(struct Node));
first->data = 10;
second->data = 20;
third->data = 30;
fourth->data = 40;
first->prev = NULL;
first->next = second;
second->prev = first;
second->next = third;
third->prev = second;
third->next = fourth;
fourth->prev = third;
fourth->next = NULL;
head = first;
temp = head;
printf("Forward: ");
while(temp->next != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
printf("%d", temp->data);
printf("\nBackward: ");
while(temp != NULL)
{
printf("%d ", temp->data);
temp = temp->prev;
}
return 0;
}
Forward
10 20 30 40
Backward
40 30 20 10
💻 Program
🧠 What is happening?
📊 Live Variables
↔️ Live Doubly Linked List
⚡ Complexity
next.prev.➕ Insertions in a Doubly Linked List
Insertion requires maintaining both prev and next links correctly.
Insert at Beginning
Set newNode->next = head, set the old head's prev to the new node,
then make the new node the head.
Insert at End
Move to the last node, connect its next to the new node,
and set newNode->prev to the old last node.
Insert at Position
Connect the new node between two existing nodes by updating four pointer relationships when both neighbours exist.
Algorithms
Insert at Beginning:
1. newNode->prev = NULL
2. newNode->next = head
3. If head != NULL
head->prev = newNode
4. head = newNode
Insert at End:
1. Move temp to last node
2. temp->next = newNode
3. newNode->prev = temp
4. newNode->next = NULL
Insert at Position:
1. Move temp to position - 1
2. newNode->prev = temp
3. newNode->next = temp->next
4. If temp->next != NULL
temp->next->prev = newNode
5. temp->next = newNode
💻 C Program — DLL Insertion Operations
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *prev;
struct Node *next;
};
struct Node *insertBeginning(struct Node *head, int value)
{
struct Node *newNode =
(struct Node *)malloc(sizeof(struct Node));
newNode->data = value;
newNode->prev = NULL;
newNode->next = head;
if(head != NULL)
head->prev = newNode;
return newNode;
}
struct Node *insertEnd(struct Node *head, int value)
{
struct Node *newNode =
(struct Node *)malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = NULL;
if(head == NULL)
{
newNode->prev = NULL;
return newNode;
}
struct Node *temp = head;
while(temp->next != NULL)
temp = temp->next;
temp->next = newNode;
newNode->prev = temp;
return head;
}
struct Node *insertAtPosition(
struct Node *head,
int value,
int position)
{
if(position <= 1)
return insertBeginning(head, value);
struct Node *temp = head;
for(int i = 1;
i < position - 1 && temp != NULL;
i++)
{
temp = temp->next;
}
if(temp == NULL)
return head;
struct Node *newNode =
(struct Node *)malloc(sizeof(struct Node));
newNode->data = value;
newNode->prev = temp;
newNode->next = temp->next;
if(temp->next != NULL)
temp->next->prev = newNode;
temp->next = newNode;
return head;
}
int main()
{
struct Node *head = NULL;
struct Node *temp;
head = insertEnd(head, 10);
head = insertEnd(head, 20);
head = insertEnd(head, 30);
head = insertBeginning(head, 5);
head = insertEnd(head, 40);
head = insertAtPosition(head, 25, 4);
temp = head;
while(temp != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
return 0;
}
Operations
Start: 10 20 30
Beginning: 5
End: 40
Position 4: 25
Output
5 10 20 25 30 40
💻 Program
🧠 What is happening?
📊 Live Variables
↔️ Live Doubly Linked List
⚡ Complexity
next and prev relationships consistent.
🗑️ Deletions in a Doubly Linked List
Deletion reconnects the previous and next neighbours before the target node is freed.
Delete Beginning
Move head to the second node and set the new head's prev to NULL.
Delete End
Move to the last node and use its prev pointer to reach the previous node directly.
Delete at Position
Reconnect target->prev and target->next to each other,
then free the target.
Algorithms
Delete Beginning:
1. temp = head
2. head = head->next
3. If head != NULL
head->prev = NULL
4. free(temp)
Delete End:
1. Move temp to last node
2. If temp->prev != NULL
temp->prev->next = NULL
Else
head = NULL
3. free(temp)
Delete at Position:
1. Move temp to target position
2. temp->prev->next = temp->next
3. If temp->next != NULL
temp->next->prev = temp->prev
4. free(temp)
💻 C Program — DLL Deletion Operations
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *prev;
struct Node *next;
};
struct Node *insertEnd(struct Node *head, int value)
{
struct Node *newNode =
(struct Node *)malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = NULL;
if(head == NULL)
{
newNode->prev = NULL;
return newNode;
}
struct Node *temp = head;
while(temp->next != NULL)
temp = temp->next;
temp->next = newNode;
newNode->prev = temp;
return head;
}
struct Node *deleteBeginning(struct Node *head)
{
if(head == NULL)
return NULL;
struct Node *temp = head;
head = head->next;
if(head != NULL)
head->prev = NULL;
free(temp);
return head;
}
struct Node *deleteEnd(struct Node *head)
{
if(head == NULL)
return NULL;
struct Node *temp = head;
while(temp->next != NULL)
temp = temp->next;
if(temp->prev != NULL)
temp->prev->next = NULL;
else
head = NULL;
free(temp);
return head;
}
struct Node *deleteAtPosition(
struct Node *head,
int position)
{
if(head == NULL)
return NULL;
if(position <= 1)
return deleteBeginning(head);
struct Node *temp = head;
for(int i = 1;
i < position && temp != NULL;
i++)
{
temp = temp->next;
}
if(temp == NULL)
return head;
temp->prev->next = temp->next;
if(temp->next != NULL)
temp->next->prev = temp->prev;
free(temp);
return head;
}
int main()
{
struct Node *head = NULL;
struct Node *temp;
head = insertEnd(head, 10);
head = insertEnd(head, 20);
head = insertEnd(head, 30);
head = insertEnd(head, 40);
head = deleteAtPosition(head, 3);
temp = head;
while(temp != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
return 0;
}
Initial List
10 20 30 40
After Deleting Position 3
10 20 40
💻 Program
🧠 What is happening?
📊 Live Variables
↔️ Live Doubly Linked List
⚡ Complexity
O(1) because its previous
and next neighbours are directly accessible.
🔄 Circular Singly Linked List
The last node does not point to NULL. Instead, it points back to the first node.
💡 Core Idea
In a circular singly linked list, the final node's next pointer stores the address of
head. Therefore, starting from head and repeatedly following next eventually
returns to the first node.
🧠 Example
Logical order: 10 → 20 → 30 → 40 → back to 10.
There is no NULL link at the end.
Node Structure
struct Node
{
int data;
struct Node *next;
};
Traversal Algorithm
1. If head == NULL
stop
2. Set temp = head
3. Do
visit(temp->data)
temp = temp->next
while(temp != head)
💻 C Program — Circular Singly Linked List
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *next;
};
struct Node *insertEnd(struct Node *head, int value)
{
struct Node *newNode =
(struct Node *)malloc(sizeof(struct Node));
newNode->data = value;
if(head == NULL)
{
newNode->next = newNode;
return newNode;
}
struct Node *temp = head;
while(temp->next != head)
temp = temp->next;
temp->next = newNode;
newNode->next = head;
return head;
}
struct Node *insertBeginning(struct Node *head, int value)
{
struct Node *newNode =
(struct Node *)malloc(sizeof(struct Node));
newNode->data = value;
if(head == NULL)
{
newNode->next = newNode;
return newNode;
}
struct Node *last = head;
while(last->next != head)
last = last->next;
newNode->next = head;
last->next = newNode;
return newNode;
}
struct Node *deleteBeginning(struct Node *head)
{
if(head == NULL)
return NULL;
if(head->next == head)
{
free(head);
return NULL;
}
struct Node *last = head;
while(last->next != head)
last = last->next;
struct Node *temp = head;
head = head->next;
last->next = head;
free(temp);
return head;
}
int main()
{
struct Node *head = NULL;
struct Node *temp;
head = insertEnd(head, 10);
head = insertEnd(head, 20);
head = insertEnd(head, 30);
head = insertBeginning(head, 5);
temp = head;
do
{
printf("%d ", temp->data);
temp = temp->next;
}
while(temp != head);
head = deleteBeginning(head);
printf("\nAfter deletion: ");
temp = head;
do
{
printf("%d ", temp->data);
temp = temp->next;
}
while(temp != head);
return 0;
}
Before Deletion
5 10 20 30
After Deletion
10 20 30
💻 Program
🧠 What is happening?
📊 Live Variables
🔄 Live Circular List
⚡ Complexity
while(temp != NULL). Since no node points to NULL, that condition can cause an infinite loop.
🔁 Circular Doubly Linked List
The first and last nodes are connected in both directions.
💡 Core Idea
In a circular doubly linked list:
tail->next = head and head->prev = tail.
This forms a two-way closed loop.
tail->next = head
head->prev = tail
Node Structure
struct Node
{
int data;
struct Node *prev;
struct Node *next;
};
Traversal
Forward:
temp = head
do
{
visit(temp->data)
temp = temp->next
}
while(temp != head)
Backward:
temp = head->prev
do
{
visit(temp->data)
temp = temp->prev
}
while(temp != head->prev)
💻 C Program — Circular Doubly Linked List
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node *prev;
struct Node *next;
};
struct Node *insertEnd(struct Node *head, int value)
{
struct Node *newNode =
(struct Node *)malloc(sizeof(struct Node));
newNode->data = value;
if(head == NULL)
{
newNode->next = newNode;
newNode->prev = newNode;
return newNode;
}
struct Node *tail = head->prev;
newNode->prev = tail;
newNode->next = head;
tail->next = newNode;
head->prev = newNode;
return head;
}
struct Node *insertBeginning(struct Node *head, int value)
{
head = insertEnd(head, value);
if(head != NULL)
head = head->prev;
return head;
}
struct Node *deleteBeginning(struct Node *head)
{
if(head == NULL)
return NULL;
if(head->next == head)
{
free(head);
return NULL;
}
struct Node *tail = head->prev;
struct Node *temp = head;
head = head->next;
head->prev = tail;
tail->next = head;
free(temp);
return head;
}
int main()
{
struct Node *head = NULL;
struct Node *temp;
head = insertEnd(head, 10);
head = insertEnd(head, 20);
head = insertEnd(head, 30);
head = insertBeginning(head, 5);
temp = head;
printf("Forward: ");
do
{
printf("%d ", temp->data);
temp = temp->next;
}
while(temp != head);
temp = head->prev;
printf("\nBackward: ");
do
{
printf("%d ", temp->data);
temp = temp->prev;
}
while(temp != head->prev);
head = deleteBeginning(head);
printf("\nAfter deletion: ");
temp = head;
do
{
printf("%d ", temp->data);
temp = temp->next;
}
while(temp != head);
return 0;
}
Forward / Backward
Forward: 5 10 20 30
Backward: 30 20 10 5
After Deleting Beginning
10 20 30
💻 Program
🧠 What is happening?
📊 Live Variables
🔁 Live Circular Doubly Linked List
⚡ Complexity
head->prev, tail is directly accessible.head->prev, making several end operations more efficient than in a basic singly linked list.
📊 Linked List Comparison
Compare the four important linked-list structures before choosing one for a problem.
| Feature | Singly Linked List | Doubly Linked List | Circular Singly Linked List | Circular Doubly Linked List |
|---|---|---|---|---|
| Node links | next | prev + next | next | prev + next |
| Last node points to | NULL | next = NULL | head | next = head |
| First node's prev | Not available | NULL | Not available | tail |
| Forward traversal | Yes | Yes | Yes, circular | Yes, circular |
| Backward traversal | No | Yes | No | Yes, circular |
| Extra pointer memory | Lower | Higher | Lower | Higher |
| Typical stopping condition | temp == NULL | temp == NULL | temp == head | temp == start/head |
| Delete known node | Needs previous node | Can reconnect prev/next directly | Needs previous node | Can reconnect prev/next directly |
When should you use each type?
Singly Linked List
Use when memory should be smaller and mainly forward traversal is needed.
Doubly Linked List
Use when backward movement and easier deletion around a known node are important.
Circular Singly
Useful for cyclic processing such as round-robin scheduling and repeating sequences.
Circular Doubly
Useful when cyclic navigation is required in both directions, such as next/previous navigation.
❓ Common Interview Questions
Think about each question first. Open the answer only when you want to verify your understanding.
1. What is the main difference between an array and a linked list?
2. Why is random access not efficient in a linked list?
3. How do you reverse a singly linked list in O(n) time and O(1) extra space?
prev, current, and nextNode.
For each node, first save current->next, then reverse current->next
to point to prev. Move prev and current forward.
Finally, set head to prev.
4. How can you find the middle node in one traversal?
5. How do you find the Nth node from the end without counting all nodes first?
6. How does Floyd's cycle detection algorithm work?
7. Why can deleting the last node of a singly linked list still be O(n) even with a tail pointer?
next points to tail.
8. What extra advantage does a doubly linked list provide?
prev and next pointers, so traversal can move in both directions.
When a target node is already known, its neighbours can often be reconnected directly during deletion.
The trade-off is additional memory and more pointer updates.
9. Why is a do-while loop often used for circular linked-list traversal?
10. What pointer mistakes commonly cause linked-list bugs?
temp != NULL as the stopping
condition for a circular list.
💻 Linked List Practice Problems
20 problems from fundamentals to circular linked lists. Each problem has 5 judge tests.
🏆 Practice Scoring
Solve without help for up to 100 points. If you open a hint, the competitive score is capped at 90. If you open the complete program, you can still finish the problem, but it is marked Completed instead of competitively solved.