Linked List
Linked List
Singly linked list is a linear data structure in which the elements are
not stored in contiguous memory locations and each element is
connected only to its next element using a pointer.
Linked List is a linear data structure, in which elements are not stored
at a contiguous location, rather they are linked using pointers. Linked
List forms a series of connected nodes, where each node stores the
data and the address of the next node.
Head and Tail: The linked list is accessed through the head node,
which points to the first node in the list. The last node in the list points
to NULL or nullptr, indicating the end of the list. This node is known as
the tail node.
printf("\n");
}
Output
123
Searching in Singly Linked List
Searching in a Singly Linked List refers to the process of looking for a
specific element or value within the elements of the linked list.
Step-by-step approach:
1. Traverse the Linked List starting from the head.
2. Check if the current node’s data matches the target value.
If a match is found, return true.
3. Otherwise, Move to the next node and repeat steps 2.
4. If the end of the list is reached without finding a match, return false.
Below is the function for searching in singly linked list:
#include <stdio.h>
#include <stdlib.h>
Step-by-step approach:
Create a new node with the given value.
Set the next pointer of the new node to the current head.
Move the head to point to the new node.
Return the new head of the linked list.
Below is the function for insertion at the beginning of singly linked list:
// Set the next pointer of the new node to the current head
new_node->next = head;
// Move the head to point to the new node
head = new_node;
Step-by-step approach:
Create a new node with the given value.
Check if the list is empty:
o If it is, make the new node the head and return.
Traverse the list until the last node is reached.
Link the new node to the current last node by setting the last node’s
next pointer to the new node.
Below is the function for insertion at the end of singly linked list:
return head;
}
c. Insertion at a Specific Position of the Singly Linked List:
To insert a node at a specific position, traverse the list to the desired
position, link the new node to the next node, and update the links
accordingly.
We mainly find the node after which we need to insert the new node. If
we encounter a NULL before reaching that node, it means that the
given position is invalid.
Below is the function for insertion at a specific position of the singly
linked list:
return head;
}
Steps-by-step approach:
Check if the head is NULL.
o If it is, return NULL (the list is empty).
Store the current head node in a temporary variable temp.
Move the head pointer to the next node.
Delete the temporary node.
Return the new head of the linked list.
Below is the function for deletion at the beginning of singly linked list:
return head;
}
b. Deletion at the End of Singly Linked List:
To delete the last node, traverse the list until the second-to-last node
and update its next field to None.
Step-by-step approach:
Check if the head is NULL.
o If it is, return NULL (the list is empty).
Check if the head’s next is NULL (only one node in the list).
o If true, delete the head and return NULL.
Traverse the list to find the second last node (second_last).
Delete the last node (the node after second_last).
Set the next pointer of the second last node to NULL.
Return the head of the linked list.
Below is the function for deletion at the end of singly linked list:
C++CJavaPythonJavaScript
if (head->next == NULL) {
free(head);
return NULL;
}
return head;
}
c. Deletion at a Specific Position of Singly Linked List:
To delete a node at a specific position, traverse the list to the desired
position, update the links to bypass the node to be deleted.
Deletion at a Specific Position of Singly Linked List
Step-by-step approach:
Check if the list is empty or the position is invalid, return if so.
If the head needs to be deleted, update the head and delete the
node.
Traverse to the node before the position to be deleted.
If the position is out of range, return.
Store the node to be deleted.
Update the links to bypass the node.
Delete the stored node.
Below is the function for deletion at a specific position of singly linked
list:
C++CJavaPythonJavaScript
return head;
}
#include <stdio.h>
#include <stdlib.h>
// Increment count by 1
count++;
// Driver code
int main() {
// Function call
printf("Count of nodes is %d", countNodes(head));
return 0;
}
Output
Count of nodes is 5
Time complexity: O(N), Where N is the size of the linked list
Auxiliary Space: O(1), As constant extra space is used.
Recursive Approach to Find the Length of a Linked List:
The idea is to use recursion by maintaining a function, say
countNodes(node) which takes a node as an argument and calls itself
with the next node until we reach the end of the Linked List. Each of the
recursive call returns 1 + count of remaining nodes.
Below is the implementation of the above approach:
#include <stdio.h>
#include <stdlib.h>
// Base Case
if (head == NULL) {
return 0;
}
// Driver code
int main() {
Output
Count of nodes is 5
Time Complexity: O(N), where N is the length of Linked List.
Auxiliary Space: O(N), Extra space is used in the recursion call stack.
Reverse a Linked List
Given a linked list, the task is to reverse the linked list by changing the
links between nodes.
Examples:
Input: Linked List = 1 -> 2 -> 3 -> 4 -> NULL
Output: Reversed Linked List = 4 -> 3 -> 2 -> 1 -> NULL
Input: Linked List = 1 -> 2 -> 3 -> 4 -> 5 -> NULL
Output: Reversed Linked List = 5 -> 4 -> 3 -> 2 -> 1 -> NULL
Input: Linked List = NULL
Output: Reversed Linked List = NULL
Input: Linked List = 1->NULL
Output: Reversed Linked List = 1->NULL
Recommended Problem
Table of Content
[Expected Approach] Using Iterative Method – O(n) Time and O(1)
Space
[Alternate Approach – 1] Using Recursion – O(n) Time and O(n)
Space
[Alternate Approach – 2] Using Stack – O(n) Time and O(n) Space
[Expected Approach] Using Iterative Method – O(n) Time and O(1)
Space:
The idea is to reverse the links of all nodes using three pointers:
prev: pointer to keep track of the previous node
curr: pointer to keep track of the current node
next: pointer to keep track of the next node
Starting from the first node, initialize curr with the head of linked list and
next with the next node of curr. Update the next pointer of curr with
prev. Finally, move the three pointer by updating prev with curr and
curr with next.
Follow the steps below to solve the problem:
Initialize three pointers prev as NULL, curr as head, and next as
NULL.
Iterate through the linked list. In a loop, do the following:
o Store the next node, next = curr -> next
o Update the next pointer of curr to prev, curr -> next = prev
o Update prev as curr and curr as next, prev = curr and curr =
next
Below is the implementation of the above approach:
// Iterative C program to reverse a linked list
#include <stdio.h>
struct Node {
int data;
struct Node* next;
};
// Given the head of a list, reverse the list and return the
// head of reversed list
struct Node* reverseList(struct Node* head) {
// Store next
next = curr->next;
int main() {
head = reverseList(head);
return 0;
}
Output
struct Node {
int data;
struct Node* next;
};
// Given the head of a list, reverse the list and return the
// head of reversed list
struct Node* reverseList(struct Node* head) {
if (head == NULL || head->next == NULL)
return head;
// reverse the rest of linked list and put the first element at the end
struct Node* rest = reverseList(head->next);
int main() {
head = reverseList(head);
return 0;
}
Output
#include <stdio.h>
struct Node {
int data;
struct Node* next;
};
// Function to create a new node
struct Node* createNode(int new_data) {
struct Node* new_node =
(struct Node*)malloc(sizeof(struct Node));
new_node->data = new_data;
new_node->next = NULL;
return new_node;
}
int main() {
head = reverseList(head);
return 0;
}
Output
A Doubly Linked List (DLL) is a type of linked list where each node
contains three components: the data, a pointer to the next node, and a
pointer to the previous node. This allows traversal in both directions—
forward and backward.
Here’s how you can define a node for a doubly linked list in C:
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
Key Characteristics
Basic Operations
1. Insertion:
o At the beginning
o At the end
o After a given node
2. Deletion:
o From the beginning
o From the end
o A specific node
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
// Function to create a new node
struct Node* createNode(int data) {
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
new_node->data = data;
new_node->next = NULL;
new_node->prev = NULL;
return new_node;
}
2. Insertion at the Beginning
void insertAtBeginning(struct Node** head_ref, int new_data) {
struct Node* new_node = createNode(new_data);
new_node->next = (*head_ref);
if (*head_ref != NULL)
(*head_ref)->prev = new_node;
*head_ref = new_node;
}
3. Insertion at the End
void insertAtEnd(struct Node** head_ref, int new_data) {
struct Node* new_node = createNode(new_data);
struct Node* last = *head_ref;
if (*head_ref == NULL) {
*head_ref = new_node;
return;
}
last->next = new_node;
new_node->prev = last;
}
4. Insertion After a Given Node
void insertAfter(struct Node* prev_node, int new_data) {
if (prev_node == NULL) {
printf("The given previous node cannot be NULL.\n");
return;
}
if (new_node->next != NULL) {
new_node->next->prev = new_node;
}
}
5. Deleting a Node
void deleteNode(struct Node** head_ref, struct Node* del) {
if (*head_ref == NULL || del == NULL) return;
if (*head_ref == del) {
*head_ref = del->next; // Change head if node to be deleted is head
}
if (del->next != NULL) {
del->next->prev = del->prev;
}
if (del->prev != NULL) {
del->prev->next = del->next;
}
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
if (new_node->next != NULL) {
new_node->next->prev = new_node;
}
}
if (*head_ref == del) {
*head_ref = del->next; // Change head if node to be deleted is head
}
if (del->next != NULL) {
del->next->prev = del->prev;
}
if (del->prev != NULL) {
del->prev->next = del->next;
}
int main() {
struct Node* head = NULL;
insertAtEnd(&head, 1);
insertAtEnd(&head, 2);
insertAtEnd(&head, 3);
printf("Initial List: ");
printListForward(head);
insertAtBeginning(&head, 0);
printf("After Inserting 0 at Beginning: ");
printListForward(head);
return 0;
}
Circular Linked Lists in C
A Circular Linked List is a variation of the linked list in which the last
node points back to the first node, forming a circle. This structure allows
for a continuous traversal of the list without needing to check for NULL at
the end.
1. Circular Singly Linked List: Each node contains a data field and
a pointer to the next node. The last node points to the first node.
2. Circular Doubly Linked List: Each node has pointers to both the
next and the previous nodes. The last node's next pointer points to
the first node, and the first node's previous pointer points to the
last node.
Structure of a Node
Here’s how you can define a node for a circular singly linked list in C:
struct Node {
int data;
struct Node* next;
};
Key Characteristics
Basic Operations
1. Insertion:
o At the beginning
o At the end
o After a given node
2. Deletion:
o From the beginning
o From the end
o A specific node
struct Node {
int data;
struct Node* next;
};
if (*head_ref == NULL) {
*head_ref = new_node;
} else {
while (temp->next != *head_ref) {
temp = temp->next; // Traverse to the last node
}
temp->next = new_node; // Link last node to new node
}
new_node->next = *head_ref; // New node points to head
*head_ref = new_node; // Update head
}
3. Insertion at the End
void insertAtEnd(struct Node** head_ref, int new_data) {
struct Node* new_node = createNode(new_data);
struct Node* temp = *head_ref;
if (*head_ref == NULL) {
*head_ref = new_node;
} else {
while (temp->next != *head_ref) {
temp = temp->next; // Traverse to the last node
}
temp->next = new_node; // Link last node to new node
}
new_node->next = *head_ref; // New node points to head
}
4. Insertion After a Given Node
void insertAfter(struct Node* prev_node, int new_data) {
if (prev_node == NULL) {
printf("The given previous node cannot be NULL.\n");
return;
}
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
if (*head_ref == NULL) {
*head_ref = new_node;
} else {
while (temp->next != *head_ref) {
temp = temp->next; // Traverse to the last node
}
temp->next = new_node; // Link last node to new node
}
new_node->next = *head_ref; // New node points to head
*head_ref = new_node; // Update head
}
if (*head_ref == NULL) {
*head_ref = new_node;
} else {
while (temp->next != *head_ref) {
temp = temp->next; // Traverse to the last node
}
temp->next = new_node; // Link last node to new node
}
new_node->next = *head_ref; // New node points to head
}
int main() {
struct Node* head = NULL;
insertAtEnd(&head, 1);
insertAtEnd(&head, 2);
insertAtEnd(&head, 3);
printf("Initial Circular List: ");
printList(head);
insertAtBeginning(&head, 0);
printf("After Inserting 0 at Beginning: ");
printList(head);
return 0;
}
Stack Implementation Using a Linked List
A stack follows the Last In First Out (LIFO) principle, where the last
element added is the first to be removed.
push(&stack, 10);
push(&stack, 20);
push(&stack, 30);
return 0;
}
Queue Implementation Using a Linked List
A queue follows the First In First Out (FIFO) principle, where the first
element added is the first to be removed.
enqueue(queue, 10);
enqueue(queue, 20);
enqueue(queue, 30);
return 0;
}