Data Structures through C Programming
UNIT 2 Syllabus
Linked Lists – introduction & types
Single Linked List: representation and operations
Double Linked List: representation and operations
Circular Linked List: representation and operations
Let us first try and understand the Memory Allocation
Memory Allocation
Static Memory Dynamic Memory
Allocation Allocation
Static Memory Allocation Dynamic Memory Allocation
Memory gets allocated at compile Memory gets allocated at runtime.
time.
Fixed size determined during Size can be changed during execution.
compilation.
Allocated in the Stack memory. Allocated in the Heap memory.
Automatically deallocated when the Must be explicitly deallocated using
function or block ends. functions like free().
Faster as memory is allocated during Slower due to allocation and deallocation
compilation. at runtime.
Less flexible because the size cannot More flexible as memory size can grow or
be changed. shrink as needed.
No risk of memory leaks but may lead Risk of memory leaks if not properly
to stack overflow if too much memory deallocated; requires careful
is used. management.
Example: Arrays Example: Linked List
int arr[10]; // Dynamically allocated structure
The size of the array must be declared struct Node *node = (struct Node
while creating the array so that the *)malloc(sizeof(struct Node));
necessary memory can get allocated.
// Freeing memory
free(node);
Difference between Arrays and Linked List
Arrays Linked List
1 Definition Array is a collection of Linked List is an ordered
elements of similar data collection of elements of
type. same type, which are
connected to each other
using pointers.
2 Size Array size is fixed- it There is no need to specify
needs to be specified at the size as the size of a
the time of array linked list can grow and
declaration. shrink according to the
requirement.
3 Memory Allocation Memory is allocated for Memory is allocated for a
the arrays at compile- linked list during the
time Static Memory execution of the program:
Allocation run-time Dynamic Memory
Allocation
4 Data Storage In an array, elements are In a linked list each node can
stored in contiguous have a different memory
memory location or location, there is no need for
consecutive manner in contiguous memory as each
the memory. node is connected with each
other with the help of a
pointer.
5 Space Utilization Space utilization of an Space utilization of a linked
array is more when list is less when compared to
compared to that of the that of the arrays.
linked list.
Different elements are
Due to contiguous stored at different
allocation, an array locations; hence, linked
can only be stored lists can be made within
where there is a large small chunks of free
block of free space is space.
available
6 Storage Array gets memory Whereas, linked list gets
allocated in memory allocated
the Stack section. in Heap section.
7 Access Array supports Random Linked List
Access, which means supports Sequential Access,
elements can be which means to access any
accessed directly using element/node in a linked list,
their index, like arr[0] for we have to sequentially
1st element, arr[6] for 7th traverse the complete linked
element etc. list, upto that element.
8 Time Complexity Time Complexity: Time Complexity:
Accessing elements in an To access nth element of a
array is fast with a linked list, time complexity
constant time complexity is O(n).
of O(1).
9 Operations In array, Insertion and
Deletion operation takes Insertion and Deletion
more time, as the operations are fast in linked
memory locations are list.
consecutive and fixed.
10 Types Array Types – Linked List Types –
Single dimensional array Single
Multidimensional array Doubly
Circular
Dataset – {23, 54, 78, 90}
With Arrays:
0 1 2 3
Since we want to store 4 numbers, we are creating an array of size 4 int
a[4]; and storing the numbers within it.
Consecutive memory gets allocated for the array.
Each element gets stored within an index. Data accessing is faster and
efficient – just by specifying the array name and index of that item.
The problem we face in this Insertion / Deletion
o When we want to insert new data into the array, you can notice that
there is no space as the array was created wit h size as 4
o When we delete any element from the array, we need to adjust the
array space according to that.
With Linked List:
Nodes are scattered in the memory but still relate
to each other.
For every data that needs to be inserted – a Node is created.
No Consecutive memory required, wherever memory space is available, the
node is created at that location.
There is no concept of Indexes.
Each Node points to the next element in the sequence.
The last node in the list points to NULL, indicating the end of the list.
Data Structures – Linked List
Key points of a Linked List
Linked List is an ordered collection of elements of same type, which are
connected to each other using pointers.
Memory is allocated at run-time (Dynamic) – can grow or shrink in size during
the program execution, hence No Wastage of memory
Data is stored in non-consecutive memory
Linked List supports Sequential Access, which means to access any
element/node in a linked list, we must sequentially traverse the complete
linked list, up to that element. Due to this for accessing nth element of a
linked list time complexity is O(n).
Insertion and Deletion operations are fast in linked list.
The size of a Linked list is variable. It grows at runtime, as more nodes are
added to it.
The linked list gets memory allocated in Heap section.
Single Circular
Linked List
Double Circular
Linked List
Single Linked List:
Navigation is forward only
Made up of Nodes -> consist of 2 parts
o Data: contains the actual data
o Address: contains the address of the next node of the list
The Nodes need not be stored in a sequential order (Contiguous memory) as
we are mapping the address of the next Node with the previous.
The 1st node is known as the HEAD node
The last node is known as the TAIL node
Each Node consist of 2 parts:
Doubly Linked List:
Forward and Backward Navigation is possible
Made up of Nodes -> consist of 3 parts
o Address: contains the address of the previous node
o Data: contains the actual data
o Address: contains the address of the next node of the list
Single Circular Linked List:
Last element is linked to the first element.
Navigation – Forward direction.
Made up of Nodes -> consist of 2 parts
o Data: contains the actual data
o Address: contains the address of the next node of the list
Double Circular Linked List:
Last element is linked to the first element.
Navigation – Forward and Backward direction.
Made up of Nodes -> consist of 3 parts
o Address: contains the address of the previous node.
o Data: contains the actual data
o Address: contains the address of the next node of the list
Single Linked List
A singly linked list is a linear data structure where each element (called a node )
contains two parts:
Data : Stores the actual value.
Pointer : Points to the next node in the list.
NODE Structure:
DATA NEXT
Memory Location
Creating Single Linked List
Structure of a Single Linked List Node
Each node consists of two components: data and a pointer (link) to the next node.
struct Node
{
int data; // Data field
struct Node* link; // Pointer to the next node
};
Function to create a New Node
struct Node* createNode(int data)
{
struct Node *newNode = malloc(sizeof(struct Node));
newNode->data = data;
newNode->link = NULL;
return newNode;
}
Explanation:
1. Memory Allocation:
struct Node* newNode = malloc(sizeof(struct Node));
2. Assigning Data:
newNode->data = data;
3. Initializing Pointer:
newNode->next = NULL;
4. Returning the Node:
return newNode;
Advantages of Singly Linked Lists
Dynamic Size :
o The size of the linked list can grow or shrink dynamically during
runtime, unlike arrays which have a fixed size.
Efficient Insertion/Deletion :
o Inserting or deleting a node is efficient (O(1)) if the position is known, as
it only involves updating pointers.
Memory Efficiency :
o Memory is allocated dynamically, so no memory is wasted on unused
slots (unlike arrays).
Ease of Implementation :
o Singly linked lists are relatively simple to implement compared to more
complex data structures like trees or graphs.
Disadvantages of Singly Linked Lists
No Random Access :
o Unlike arrays, you cannot access elements directly by index. You must
traverse the list sequentially to reach a specific node (O(n)).
Extra Memory for Pointers :
o Each node requires additional memory to store the pointer (link), which
increases memory overhead.
Traversal in One Direction Only :
o Singly linked lists can only be traversed in one direction (forward),
making reverse traversal impossible without additional logic.
Complexity in Certain Operations :
o Operations like finding the middle node or reversing the list require
additional effort compared to arrays.
Real-Time Usage of Singly Linked Lists
Implementing Stacks and Queues :
o Singly linked lists are often used to implement stack and queue data
structures because they allow efficient insertion and deletion at the
beginning or end.
Memory Management :
o Operating systems use linked lists to manage free blocks of memory
(e.g., allocating and deallocating memory dynamically).
Browser History :
o Web browsers use singly linked lists to maintain a history of visited
pages, where each node represents a URL.
Music Playlists :
o Music players use linked lists to store playlists, allowing users to move
forward (and backward in doubly linked lists) between songs.
Symbol Tables in Compilers :
o Compilers use linked lists to store symbols (e.g., variable names,
function names) during the compilation process.
Undo Operations :
o Applications like text editors use linked lists to implement undo
functionality, where each node represents a previous state.
Hash Tables with Chaining :
o In hash tables, linked lists are used to handle collisions by chaining
multiple elements that hash to the same index.
----------------------------------------------------------------------------------------
Algorithm for Insertions
Step-by-Step Execution Algorithm
1. Include Header Files
stdio.h is included for input/output operations (e.g., printf(), scanf()).
stdlib.h is included for dynamic memory allocation (e.g., malloc(), free()).
2. Define the Node Structure
A structure named Node is defined:
o data: Stores the integer value of the node.
o next: A pointer to the next node in the list.
3. Declare the Global Head Pointer
A global pointer head is declared and initialized to NULL. This pointer will
always point to the first node in the linked list.
4. Function: Insert at the Beginning
Allocate memory for a new node using malloc.
Assign the given value to the data field of the new node.
Set the next pointer of the new node to the current head.
Update the head pointer to point to the new node.
Code Snippet:
void insertAtBeginning(int value)
{
struct Node* newNode = malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = head;
head = newNode;
}
5. Function: Insert at the End
Allocate memory for a new node using malloc.
Assign the given value to the data field of the new node.
Set the next pointer of the new node to NULL.
If the list is empty (head == NULL), make the new node the head.
Otherwise, traverse the list to find the last node (where temp->next ==
NULL).
Link the last node's next pointer to the new node.
Code Snippet:
void insertAtEnd(int value)
{
struct Node* newNode = malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = NULL;
if (head == NULL)
{
head = newNode;
}
else
{
struct Node* temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = newNode;
}
}
6. Function: Insert at a Specific Position
Allocate memory for a new node using malloc.
Assign the given value to the data field of the new node.
If the position is 1, make the new node the head by updating its next pointer
to the current head and setting head to the new node.
Otherwise, traverse the list to the node just before the desired position.
If the traversal reaches NULL before the desired position, print "Invalid
position".
Otherwise, link the new node between the current node (temp) and its next
node (temp->next).
Code Snippet:
void insertAtPosition(int value, int position)
{
struct Node* newNode = malloc(sizeof(struct Node));
newNode->data = value;
if (position == 1)
{
newNode->next = head;
head = newNode;
return;
}
struct Node* temp = head;
for (int i=1;i<position-1 && temp != NULL;i++)
{
temp = temp->next;
}
if (temp == NULL)
{
printf("Invalid position\n");
}
else
{
newNode->next = temp->next;
temp->next = newNode;
}
}
7. Function: Traversal
Check if the list is empty (head == NULL). If so, print "List is empty" and
return.
Start from the head node and traverse the list using a temporary pointer
temp.
Print the data field of each node followed by "->".
Stop when temp becomes NULL and print "NULL" to indicate the end of the
list.
Code Snippet:
void traversal()
{
struct Node* temp = head;
if (temp == NULL)
{
printf("List is empty\n");
return;
}
printf("Linked List: ");
while (temp != NULL)
{
printf("%d -> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
8. Main Function
Combine all operations to demonstrate insertion and traversal.
Complete Code for Insertion
#include <stdio.h>
#include <stdlib.h>
struct Node
{
int data;
struct Node* next;
};
struct Node* head = NULL;
void insertAtBeginning(int value)
{
struct Node* newNode = malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = head;
head = newNode;
}
void insertAtEnd(int value)
{
struct Node* newNode = malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = NULL;
if (head == NULL)
{
head = newNode;
}
else
{
struct Node* temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = newNode;
}
}
void insertAtPosition(int value, int position)
{
struct Node* newNode = malloc(sizeof(struct Node));
newNode->data = value;
if (position == 1)
{
newNode->next = head;
head = newNode;
return;
}
struct Node* temp = head;
for (int i=1;i<position-1 && temp != NULL;i++)
{
temp = temp->next;
}
if (temp == NULL)
{
printf("Invalid position\n");
}
else
{
newNode->next = temp->next;
temp->next = newNode;
}
}
void traversal()
{
struct Node* temp = head;
if (temp == NULL)
{
printf("List is empty\n");
return;
}
printf("Linked List: ");
while (temp != NULL)
{
printf("%d -> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
int main()
{
printf("Inserting Head Node");
insertAtBeginning(55);
insertAtBeginning(80);
printf("\nAfter Insertion ");
traversal();
printf("Inserting Tail Node");
insertAtEnd(18);
insertAtEnd(24);
printf("\nAfter Insertion ");
traversal();
printf("Inserting at a Specific Position");
insertAtPosition(27,1);
insertAtPosition(100,3);
printf("\nAfter Insertion ");
traversal();
return 0;
}
Output:
Inserting Head Node
After Insertion Linked List: 80 -> 55 -> NULL
Inserting Tail Node
After Insertion Linked List: 80 -> 55 -> 18 -> 24 -> NULL
Inserting at a Specific Position
After Insertion Linked List: 27 -> 80 -> 100 -> 55 -> 18 -> 24 -> NULL
----------------------------------------------------------------------------------------
Algorithm for Traversal
Traversal refers to the process of visiting each element (or node) in a data
structure exactly once, typically in a systematic order, to perform some
operation (e.g., printing, searching, updating).
Traversal is one of the fundamental operations in data structures and is used
extensively in algorithms.
Code:
/* Program: Traversal Operation - Counting the Number of Nodes present within the
Linked List */
// Adding the Header Files
#include<stdio.h>
#include<stdlib.h>
// Creating the Self-referential Structure - Node
struct Node
{
int data;
struct Node *link;
};
// Function for Counting the Number of Nodes within the Linked List
void count_Nodes(struct Node *head)
{
// Checking if the Linked List is Empty
if(head == NULL)
printf("Linked List is Empty\n");
// Assigning the Count variable to 0
// This will be incremented by 1 for each Node
int i = 0;
// Creating a Structure Pointer and assigning the value as NULL
struct Node *p = NULL;
// Making the Structure Pointer point to the HEAD Node
p = head;
while(p != NULL)
{
i++;
printf("\nThe values of %d Node: %d", i, p->data);
p = p->link;
}
printf("\nThere are %d Nodes in the Linked List",i);
}
// Main Code
int main()
{
int data; // temp variable
//Creating the 1st Node - HEAD
struct Node *head = malloc(sizeof(struct Node));
printf("Enter the Integer Value: ");
scanf("%d",&data);
head->data = data;
head->link = NULL;
// Creating the 2nd Node
struct Node *current = malloc(sizeof(struct Node));
printf("\nEnter the Integer Value: ");
scanf("%d",&data);
current->data = data;
current->link = NULL;
head->link = current;
// Creating the 3rd Node
current = malloc(sizeof(struct Node));
printf("\nEnter the Integer Value: ");
scanf("%d",&data);
current->data = data;
current->link = NULL;
head->link->link = current;
// Creating the 4th Node
current = malloc(sizeof(struct Node));
printf("\nEnter the Integer Value: ");
scanf("%d",&data);
current->data = data;
current->link = NULL;
head->link->link->link = current;
// Counting the Nodes
count_Nodes(head);
return 0;
}
Input:
Enter the Integer Value: 48
Enter the Integer Value: 98
Enter the Integer Value: 52
Enter the Integer Value: 40
Output:
The values of 1 Node: 48
The values of 2 Node: 98
The values of 3 Node: 52
The values of 4 Node: 40
There are 4 Nodes in the Linked List
Explanation:
This is the Linked list that needs to be traversed.
1st Node: 1000 2nd Node: 3000 3rd Node: 8000 4th Node: 2200
48 3000 98 8000 52 2200 40 NULL
Data Link Data Link Data Link Data Link
HEAD
Step 1: Declare Variables
Step 2: Create the First Node (HEAD)
Step 3: Create the Second Node
Step 4: Create the Third Node
Step 5: Create the Fourth Node
Step 6: Call the count_Nodes Function
count_Nodes() Function Explanation:
Create a pointer ptr and assign the value of the head node to it.
So now both head and ptr will be pointing to the address 1000
Loop condition: If the ptr value does not reach NULL – the loop will continue its
execution.
o 1st Loop execution:
i’s value has increased by 1 and becomes: i = 1
We have the ptr point to the head Node(1st); So ptr->data will
print the data of the head note 45
p = p->link will make the pointer point to the next Node
o 2nd Loop execution:
i’s value has increased by 1 and becomes: i = 2
We have the ptr point to the next Node(2nd); So ptr->data will print
the 2nd node 98
p = p->link will make the pointer point to the next Node
o 3rd Loop execution:
i’s value has increased by 1 and becomes: i = 3
We have the ptr point to the next Node (3rd); So ptr->data will
print the data of the head note 3
p = p->link will make the pointer point to the next Node
o 4th Loop execution:
i’s value has increased by 1 and becomes: i = 4
We have the ptr point to the next Node(4th); So ptr->data will print
the 2nd node 98
p = p->link will make the pointer point to the next Node
----------------------------------------------------------------------------------------
Algorithm for Deletions
#include <stdio.h>
#include <stdlib.h>
// Node structure for the singly linked list
struct Node
{
int data;
struct Node* next;
};
// Global head pointer
struct Node* head = NULL;
// Function to create a new node
struct Node* createNode(int data)
{
struct Node* newNode = malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// Function to insert at the end (for testing purposes)
void insertAtEnd(int data)
{
struct Node* newNode = createNode(data);
if (head == NULL)
{
head = newNode; // If the list is empty, the new node becomes the head
return;
}
struct Node* temp = head;
while (temp->next != NULL)
{
temp = temp->next; // Traverse to the last node
}
temp->next = newNode; // Link the last node to the new node
}
// Function to display the linked list
void displayList()
{
struct Node* temp = head;
if (temp == NULL)
{
printf("The list is empty.\n");
return;
}
printf("Linked List: ");
while (temp != NULL)
{
printf("%d -> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
// Function to delete the first node
void deleteFirst()
{
if (head == NULL)
{
printf("The list is already empty. Nothing to delete.\n");
return;
}
struct Node* temp = head;
head = head->next; // Move the head to the next node
free(temp); // Free the memory of the old head
printf("Deleted the first node.\n");
}
// Function to delete the last node
void deleteLast()
{
if (head == NULL)
{
printf("The list is already empty. Nothing to delete.\n");
return;
}
if (head->next == NULL)
{
free(head); // If there's only one node, free it
head = NULL; // Set head to NULL
printf("Deleted the last node.\n");
return;
}
struct Node* temp = head;
while (temp->next->next != NULL)
{
temp = temp->next; // Traverse to the second-to-last node
}
free(temp->next); // Free the last node
temp->next = NULL; // Update the second-to-last node's next to NULL
printf("Deleted the last node.\n");
}
// Function to delete a node by value
void deleteByValue(int value)
{
if (head == NULL)
{
printf("The list is empty. Nothing to delete.\n");
return;
}
if (head->data == value)
{
struct Node* temp = head;
head = head->next; // Move the head to the next node
free(temp); // Free the old head
printf("Deleted node with value %d.\n", value);
return;
}
struct Node* temp = head;
while (temp->next != NULL && temp->next->data != value)
{
temp = temp->next; // Traverse to find the node before the target
}
if (temp->next == NULL)
{
printf("Value %d not found in the list.\n", value);
return;
}
struct Node* nodeToDelete = temp->next;
temp->next = temp->next->next; // Bypass the target node
free(nodeToDelete); // Free the target node
printf("Deleted node with value %d.\n", value);
}
// Function to delete a node at a specific position
void deleteAtPosition(int position)
{
if (head == NULL)
{
printf("The list is empty. Nothing to delete.\n");
return;
}
if (position == 0)
{
struct Node* temp = head;
head = head->next; // Move the head to the next node
free(temp); // Free the old head
printf("Deleted node at position %d.\n", position);
return;
}
struct Node* temp = head;
int currentPos = 0;
while (temp->next != NULL && currentPos < position - 1) {
temp = temp->next; // Traverse to the node before the target
currentPos++;
}
if (temp->next == NULL)
{
printf("Position %d is out of range.\n", position);
return;
}
struct Node* nodeToDelete = temp->next;
temp->next = temp->next->next; // Bypass the target node
free(nodeToDelete); // Free the target node
printf("Deleted node at position %d.\n", position);
}
// Main function to test the deletion operations
int main()
{
// Insert some elements into the list
insertAtEnd(10);
insertAtEnd(20);
insertAtEnd(30);
insertAtEnd(40);
// Display the initial list
printf("Initial List:\n");
displayList();
// Test deletion operations
printf("\nDeleting the first node:\n");
deleteFirst();
displayList();
printf("\nDeleting the last node:\n");
deleteLast();
displayList();
printf("\nDeleting node with value 20:\n");
deleteByValue(20);
displayList();
printf("\nDeleting node at position 1:\n");
deleteAtPosition(1);
displayList();
return 0;
}
Output:
Initial List:
Linked List: 10 -> 20 -> 30 -> 40 -> NULL
Deleting the first node:
Deleted the first node.
Linked List: 20 -> 30 -> 40 -> NULL
Deleting the last node:
Deleted the last node.
Linked List: 20 -> 30 -> NULL
Deleting node with value 20:
Deleted node with value 20.
Linked List: 30 -> NULL
Deleting node at position 1:
Position 1 is out of range.
Linked List: 30 -> NULL
----------------------------------------------------------------------------------------
Single Linked List – Linear Search Implementation
// Inclusion of Header Files
#include <stdio.h>
#include <stdlib.h>
// Creating the Node Structure
struct Node
{
int data;
struct Node* next;
};
// Global Header Variable
struct Node* head = NULL;
// Function to insert data at the End
void insertAtEnd(int value)
{
struct Node* newNode = malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = NULL;
if (head == NULL)
{
head = newNode;
}
else
{
struct Node* temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = newNode;
}
}
// Function to display the values of the Linked List
void displayList()
{
struct Node* temp = head;
if (temp == NULL)
{
printf("List is empty\n");
return;
}
printf("Linked List: ");
while (temp != NULL)
{
printf("%d -> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
// Function to implement Linear Search
int linearSearch(struct Node *head, int key)
{
// Start from the head
struct Node* current = head;
int position = 0;
// Traverse the list
while (current != NULL)
{
if (current->data == key)
{
// Return the position (0-based index)
return position;
}
current = current->next;
position++;
}
// Key not found
return -1;
}
// Drive Code
int main()
{
// Inserting data
insertAtEnd(89);
insertAtEnd(55);
insertAtEnd(10);
insertAtEnd(25);
insertAtEnd(78);
insertAtEnd(34);
// Displaying the data
printf("\nLinked List: ");
displayList();
// Value to search for within the List
int key;
printf("Enter the element to search: ");
scanf("%d", &key);
// Calling the Linear Search Function
int result = linearSearch(head, key);
// Printing the Result
if (result != -1)
{
printf("Element %d found at position %d (0-based index).\n", key, result);
}
else
{
printf("Element %d not found in the linked list.\n", key);
}
return 0;
}
Output:
Linked List: Linked List: 89 -> 55 -> 10 -> 25 -> 78 -> 34 -> NULL
Enter the element to search: Element 25 found at position 3 (0-based index).
Output:
Linked List: Linked List: 89 -> 55 -> 10 -> 25 -> 78 -> 34 -> NULL
Enter the element to search: Element 250 not found in the linked list.
----------------------------------------------------------------------------------------
Single Linked List – Bubble Sort Implementation
// Inclusion of Header Files
#include <stdio.h>
#include <stdlib.h>
// Definition of the Node structure
struct Node
{
int data; // Data field to store the value
struct Node* next; // Pointer to the next node in the list
};
// Global head pointer
struct Node *head = NULL;
// Function to create a new node
struct Node *createNode(int value)
{
struct Node *newNode = malloc(sizeof(struct Node));
if (newNode == NULL)
{
printf("Memory allocation failed.\n");
exit(1);
}
newNode->data = value;
newNode->next = NULL;
return newNode;
}
// Function to insert a node at the end of the list
void insertAtEnd(int value)
{
struct Node *newNode = createNode(value);
if (head == NULL)
{
head = newNode; // If the list is empty, set head to the new node
}
else
{
struct Node *temp = head;
while (temp->next != NULL)
{
temp = temp->next; // Traverse to the last node
}
temp->next = newNode; // Link the new node at the end
}
}
// Function to perform Bubble Sort on the linked list
void bubbleSort()
{
if (head == NULL || head->next == NULL)
{
return; // List is empty or has only one node, no sorting needed
}
int swapped;
struct Node *current;
struct Node *last = NULL; // To track the last node of the unsorted portion
do
{
swapped = 0; // Reset the swap flag
current = head;
while (current->next != last)
{
if (current->data > current->next->data)
{
// Swap the data of adjacent nodes
int temp = current->data;
current->data = current->next->data;
current->next->data = temp;
swapped = 1; // Mark that a swap occurred
}
current = current->next; // Move to the next node
}
last = current; // Update the last node of the unsorted portion
} while (swapped); // Repeat until no swaps occur in a pass
}
// Function to print the linked list
void printList()
{
struct Node *temp = head;
while (temp != NULL)
{
printf("%d -> ", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
// Function to free the memory allocated for the linked list
void freeList()
{
struct Node *temp;
while (head != NULL)
{
temp = head;
head = head->next;
free(temp);
}
}
// Drive Code
int main()
{
// Insert elements into the linked list
insertAtEnd(85);
insertAtEnd(45);
insertAtEnd(30);
insertAtEnd(28);
insertAtEnd(78);
insertAtEnd(62);
insertAtEnd(110);
// Before Sorting - Printing the List
printf("Original List:\n");
printList();
// Perform Bubble Sort
bubbleSort();
// After Sorting - Printing the List
printf("Sorted List:\n");
printList();
// Free the allocated memory
freeList();
return 0;
}
Output:
Original List: 85 -> 45 -> 30 -> 28 -> 78 -> 62 -> 110 -> NULL
Sorted List: 28 -> 30 -> 45 -> 62 -> 78 -> 85 -> 110 -> NULL
----------------------------------------------------------------------------------------
Doubly Linked List
In a Single Linked List there is only one link field hence traversing is done only
in one direction. So, given the address of a node x, only those nodes which
follow x are reachable but, the nodes that precede x are not reachable.
Each node of a Doubly Linked List contains three components:
Data: The value or information stored in the node.
Previous Pointer: A pointer to the previous node in the list.
Next Pointer: A pointer to the next node in the list.
struct Node
{
int data; // Data stored in the node
struct Node *prev; // Pointer to the previous node
struct Node *next; // Pointer to the next node
};
NODE Structure:
PREVIOUS DATA NEXT
Memory Location
A doubly linked list allows traversal in both directions (forward and backward).
This bidirectional capability makes it more versatile than a singly linked list.
Doubly Linked List – Practical examples
Doubly linked lists are widely used in various applications, including:
Browser History Management :
o Forward and backward navigation in web browsers.
Undo/Redo Operations :
o Text editors and graphic design tools use doubly linked lists to
implement undo and redo functionality.
Music Players :
o Playlists often use doubly linked lists to allow users to move forward and
backward through songs.
Operating Systems :
o Task scheduling and memory management systems may use doubly
linked lists for efficient resource allocation.
LRU Cache Implementation :
o Least Recently Used (LRU) caches often use doubly linked lists to
maintain the order of elements efficiently.
Advantages of Doubly Linked Lists
Bidirectional Traversal :
o You can traverse the list in both forward and backward directions, which
is not possible in a singly linked list.
Efficient Deletion :
o Deleting a node is easier because you can directly access the previous
node using the prev pointer.
Flexible Operations :
o Insertion and deletion at both end (head and tail) can be done in
constant time (O(1)), provided you maintain pointers to both ends.
Dynamic Size :
o Like other linked lists, doubly linked lists can grow or shrink dynamically
during runtime.
Disadvantages of Doubly Linked Lists
Extra Memory Usage :
o Each node requires an additional pointer (prev), which increases
memory consumption compared to singly linked lists.
Complexity :
o Managing two pointers (prev and next) makes operations slightly more
complex than in singly linked lists.
Slower Performance :
o Due to the extra pointer, memory access patterns may be less cache-
friendly, leading to slightly slower performance in some cases.
More Code :
o Implementing operations like insertion and deletion requires handling
both prev and next pointers, which increases the amount of code.
Creating Double Linked List
Structure of a Double Linked List Node:
struct Node
{
int data;
struct Node* prev;
struct Node* next;
};
This defines a Node structure with three members:
int data: Stores the value or data for the node.
struct Node* prev: A pointer to the previous node in the doubly linked list.
struct Node* next: A pointer to the next node in the doubly linked list.
Function to create a New Node:
struct Node *createNode(int data)
{
struct Node* newNode = malloc(sizeof(struct Node));
newNode->data = data;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}
Explanation:
5. Memory Allocation:
struct Node* newNode = malloc(sizeof(struct Node));
6. Assigning Data:
newNode->data = data;
7. Initializing Pointers:
newNode->prev = NULL;
newNode->next = NULL;
8. Returning the Node:
return newNode;
Algorithm for Insertions
Assumptions:
The doubly linked list has a global pointer head those points to the first node in the
list.
Each node in the list has three components:
data: Stores the value of the node.
next: A pointer to the next node in the list.
prev: A pointer to the previous node in the list.
The function createNode(int data) creates and returns a new node with the given
data, initializes its next and prev pointers to NULL, and returns the pointer to this
new node.
Insertion at the Start
The insertAtStart() function inserts a new node at the beginning of a doubly linked
list by:
Creating a new node.
Checking if the list is empty; if so, making the new node the head.
Otherwise, updating the next pointer of the new node, the prev pointer of the
current head, and finally updating the global head pointer to the new node.
1. Create a new node
2. Check if the list is empty
The function checks if the head pointer is NULL. If head is NULL, it means
the list is currently empty.
In this case, the new node becomes the first (and only) node in the list by
assigning head to point to newNode.
The function then exits early using return.
3. Point the new node's next to the current head
If the list is not empty, the next pointer of the new node is updated to point
to the current head node.
This establishes the forward link from the new node to the existing list.
4. Update the current head's prev pointer
The prev pointer of the current head node is updated to point to the new
node.
This establishes the backward link from the current head to the new node.
5. Update the global head pointer
Finally, the global head pointer is updated to point to the new node.
This makes the new node the first node in the list.
Code:
void insertAtStart(int data)
{
struct Node* newNode = createNode(data); // Create a new node
if (head == NULL)
{
head = newNode; // If the list is empty, the new node becomes the head
return;
}
newNode->next = head; // Point the new node's next to the current head
head->prev = newNode; // Point the current head's prev to the new node
head = newNode; // Update the global head to the new node
}
Linked List
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 5000
1. Create - New Node Node: 5000
Node: 8000
3000 36 NULL
NULL 100 NULL
2. New Node: Set the next pointer of the new node to the current head
Node: 8000
NULL 100 1000
3. Set the prev pointer of the current head to the new node
HEAD Node: 1000 Node: 3000
8000 50 3000 1000 78 5000
New Node Node: 5000
3000 36 NULL
Node: 8000
NULL 100 1000
4. Update the head to point to the new node.
Node: 1000 Node: 3000
8000 50 3000 1000 78 5000
Node: 5000
HEAD Node: 8000 3000 36 NULL
NULL 100 1000
----------------------------------------------------------------------------------------
Insertion at the End
The function insertAtEnd() Function is designed to insert a new node at the end of
a doubly linked list.
1. Create a new node with the given data.
2. Check if the list is empty :
a. If empty, make the new node the head of the list and exit.
3. Traverse to the last node of the list.
4. Link the new node to the last node:
a. Update the next pointer of the last node to point to the new node.
b. Update the prev pointer of the new node to point to the last node.
1. Create a New Node
2. Check if the List is Empty
The function checks if the head pointer is NULL. If it is, this means the list
is empty.
In this case:
The new node becomes the first (and only) node in the list by assigning
head = newNode.
The function then returns immediately since there's nothing more to do.
3. Traverse to the Last Node
If the list is not empty, the function initializes a temporary pointer temp to
point to the head of the list.
It then enters a while loop to traverse the list until it reaches the last node.
This is done by repeatedly moving temp to temp->next until temp->next is
NULL.
4. Link the New Node to the Last Node
Once the traversal is complete, temp points to the last node in the list.
The following two steps link the new node to the list:
temp->next = newNode: The next pointer of the current last node (temp)
is updated to point to the new node (newNode).
newNode->prev = temp: The prev pointer of the new node (newNode) is
updated to point back to the current last node (temp).
Code:
// Function to insert at the end
void insertAtEnd(int data)
{
struct Node* newNode = createNode(data); // Create a new node
if (head == NULL)
{
head = newNode; // If the list is empty, the new node becomes the head
return;
}
struct Node* temp = head;
while (temp->next != NULL)
{
temp = temp->next; // Traverse to the last node
}
temp->next = newNode; // Point the last node's next to the new node
newNode->prev = temp; // Point the new node's prev to the last node
}
Linked List
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 5000
1. Create - New Node Node: 5000
Node: 8000 3000 36 NULL
NULL 15 NULL
2. Create a temp pointer and make it point to HEAD
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 5000
New Node Node: 5000
temp
Node: 8000 3000 36 NULL
NULL 15 NULL
3. Loop execution
1st time Loop execution:
1000 != NULL True : temp = 3000 while (temp->next != NULL)
2nd time Loop execution: {
3000 != NULL True : temp = 5000 temp = temp->next;
}
3rd time Loop execution:
NULL != NULL False – Loop terminates
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 5000
New Node Node: 5000
Node: 8000 3000 36 NULL
NULL 15 NULL
temp
4. temp->next = newNode;
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 5000
New Node Node: 5000
Node: 8000
3000 36 8000
NULL 15 NULL
5. newNode->prev = temp; temp
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 5000
Node: 5000 Node: 8000
5000 15 NULL
3000 36 8000
temp Newnode
----------------------------------------------------------------------------------------
Insertion at a Specific Position
Create a new node with the given data.
If the position is 1 (insert at the start):
o Perform the same steps as "Insertion at the Start."
Otherwise:
o Traverse the list to find the node at the specified position.
o Adjust the prev and next pointers of the surrounding nodes to include
the new node.
Return the updated head.
void insertAtPosition(int data, int position)
{
struct Node* newNode = malloc(sizeof(struct Node));
newNode->data = data;
if (position == 1)
{
newNode->next = head;
if (head != NULL)
{
head->prev = newNode;
}
head = newNode;
return;
}
struct Node* temp = head;
int currentPosition = 1;
while (temp != NULL && currentPosition < position - 1)
{
temp = temp->next;
currentPosition++;
}
if (temp == NULL)
{
printf("Invalid position\n");
free(newNode); // Free the unused node
return;
}
newNode->next = temp->next;
newNode->prev = temp;
if (temp->next != NULL)
{
temp->next->prev = newNode;
}
temp->next = newNode;
}
Linked List
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 5000
Create - New Node Node: 5000
3000 36 NULL
Node: 8000
NULL 25 NULL
If Position to insert the new node is 1
newNode->next = head;
Node: 8000
NULL 25 1000
head->prev = newNode;
HEAD Node: 1000 Node: 3000
8000 50 3000 1000 78 5000
head = newNode; Node: 5000
HEAD Node: 8000 3000 36 NULL
NULL 25 1000
Node: 1000 Node: 3000
8000 50 3000 1000 78 5000
Node: 5000
HEAD Node: 8000
3000 36 NULL
NULL 25 1000
----------------------------------------------------------------------------------------
Linked List
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 5000
Create - New Node Node: 5000
temp
Node: 8000 3000 36 NULL
NULL 25 NULL
Position to insert the new node is 3
int currentPosition = 1;
while (temp != NULL && currentPosition < position - 1)
{
temp = temp->next;
currentPosition++;
}
1st Loop execution:
1000 != NULL && 1 < 2 True: temp= 3000, currentPosition=2
2 Loop execution:
nd
3000 !+ NULL & 2 < 2 False – Loop terminates
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 5000
Create - New Node Node: 5000
temp
Node: 8000 3000 36 NULL
NULL 25 NULL
newNode->next = temp->next;
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 5000
Node: 5000
temp
NEW Node: 8000
3000 36 NULL
NULL 25 5000
newNode->prev = temp;
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 5000
Node: 5000
temp
NEW Node: 8000
3000 36 NULL
3000 25 5000
5000 != NULL True: temp->next->prev = newNode;
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 5000
Node: 5000
temp
NEW Node: 8000
8000 36 NULL
3000 25 5000
temp->next = newNode;
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 8000
Node: 5000
temp
NEW Node: 8000
8000 36 NULL
3000 25 5000
temp->next = newNode;
HEAD Node: 1000 Node: 3000
NULL 50 3000 1000 78 8000
Node: 8000 Node: 5000
3000 25 5000 8000 36 NULL
----------------------------------------------------------------------------------------
Complete Code for Doubly Linked List Insertion Operation
Insertion at Beginning of the Double Linked List
Insertion at the End of the Double Linked List
Insertion at a Specific Position of the Double Linked List
#include <stdio.h>
#include <stdlib.h>
// Define the structure for a node
struct Node
{
int data;
struct Node* prev;
struct Node* next;
};
// Global head node
struct Node* head = NULL;
// Function to create a new node
struct Node* createNode(int data)
{
struct Node* newNode = malloc(sizeof(struct Node));
newNode->data = data;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}
// Function to insert at the start
void insertAtStart(int data)
{
struct Node* newNode = createNode(data); // Create a new node
if (head == NULL)
{
head = newNode; // If the list is empty, the new node becomes the head
return;
}
newNode->next = head; // Point the new node's next to the current head
head->prev = newNode; // Point the current head's prev to the new node
head = newNode; // Update the global head to the new node
}
// Function to insert at the end
void insertAtEnd(int data)
{
struct Node* newNode = createNode(data); // Create a new node
if (head == NULL)
{
head = newNode; // If the list is empty, the new node becomes the head
return;
}
struct Node* temp = head;
while (temp->next != NULL)
{
temp = temp->next; // Traverse to the last node
}
temp->next = newNode; // Point the last node's next to the new node
newNode->prev = temp; // Point the new node's prev to the last node
}
// Function to insert at a specific position
void insertAtPosition(int data, int position)
{
struct Node* newNode = createNode(data); // Create a new node
if (position == 1)
{
// Insert at the start
newNode->next = head;
if (head != NULL)
{
head->prev = newNode;
}
head = newNode; // Update the global head
return;
}
struct Node* temp = head;
int currentPosition = 1;
while (temp != NULL && currentPosition < position - 1)
{
temp = temp->next;
currentPosition++;
}
if (temp == NULL)
{
printf("Invalid position\n");
free(newNode); // Free the unused node
return;
}
// Insert the new node after temp
newNode->next = temp->next;
newNode->prev = temp;
if (temp->next != NULL)
{
temp->next->prev = newNode;
}
temp->next = newNode;
}
// Function to print the list forward
void printList()
{
struct Node* temp = head;
while (temp != NULL)
{
printf("%d -> ", temp->data);
temp = temp->next;
}
printf("NULL \n");
}
// Main function to demonstrate the implementation
int main()
{
// Insert at beginning of the List
insertAtStart(10);
insertAtStart(40);
insertAtStart(5);
printf("After Inserting at Start - ");
printf("List: ");
printList();
printf("\n");
// Insert at the end of the List
insertAtEnd(20);
insertAtEnd(80);
insertAtEnd(30);
printf("After Inserting at End - ");
printf("List: ");
printList();
printf("\n");
// Insert at a specific position
insertAtPosition(15, 3);
printf("After Inserting at Position 3 - ");
printf("List: ");
printList();
insertAtPosition(40, 6);
printf("After Inserting at Position 6 - ");
printf("List: ");
printList();
printf("\n");
return 0;
}
Output:
After Inserting at Start - List: 5->10->10->NULL
After Inserting at End - List: 5->10->10->20->20->30->NULL
After Inserting at Position 3 - List: 5->10->15->10->20->20->30->NULL
After Inserting at Position 6 - List: 5->10->15->10->20->40->20->30->NULL
----------------------------------------------------------------------------------------
Algorithm for Traversal
With Double Linked list we can traverse in both Forward & Backward directions.
1. Structure Definition
A doubly linked list node is defined using the struct Node:
o data: Stores the value of the node.
o prev: Pointer to the previous node in the list.
o next: Pointer to the next node in the list.
2. Global Head Node
A global pointer head is initialized to NULL. This pointer keeps track of the first
node in the list.
3. Node Creation
The function createNode(int data) dynamically allocates memory for a new
node using malloc.
o It initializes the data field with the provided value.
o Both prev and next pointers are set to NULL since the node is initially
isolated.
4. Insertion at the End
The function insertAtEnd(int data) adds a new node at the end of the list:
o If the list is empty (head == NULL), the new node becomes the head.
o Otherwise:
Traverse the list starting from the head until the last node is
reached (temp->next == NULL).
Update the next pointer of the last node to point to the new node.
Set the prev pointer of the new node to point to the last node.
5. Forward Traversal
The function traverseForward() prints the list in the forward direction:
o Start from the head node.
o Traverse the list using the next pointer until NULL is encountered.
o Print each node's data value during traversal.
6. Backward Traversal
The function traverseBackward() prints the list in the backward direction:
o Start from the head node and traverse to the last node using the next
pointer.
o Once at the last node, traverse backward using the prev pointer until
NULL is encountered.
o Print each node's data value during backward traversal.
7. Main Function
The main() function demonstrates the implementation:
o Inserts four elements (10, 20, 30, 40) into the list using insertAtEnd().
o Calls traverseForward() to print the list in the forward direction.
o Calls traverseBackward() to print the list in the backward direction.
Complete Code: Traversal
#include <stdio.h>
#include <stdlib.h>
// Define the structure for a node
struct Node
{
int data;
struct Node* prev;
struct Node* next;
};
// Global head node
struct Node* head = NULL;
// Function to create a new node
struct Node* createNode(int data)
{
struct Node* newNode = malloc(sizeof(struct Node));
if (newNode == NULL) // Check if malloc failed
{
printf("Memory allocation failed. Exiting...\n");
exit(1); // Exit the program with an error code
}
newNode->data = data;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}
// Function to insert at the end (to populate the list)
void insertAtEnd(int data)
{
struct Node* newNode = createNode(data);
if (head == NULL)
{
head = newNode; // If the list is empty, the new node becomes the head
return;
}
struct Node* temp = head;
while (temp->next != NULL)
{
temp = temp->next; // Traverse to the last node
}
temp->next = newNode; // Point the last node's next to the new node
newNode->prev = temp; // Point the new node's prev to the last node
}
// Function to traverse the list in forward direction
void traverseForward()
{
struct Node* temp = head;
if (temp == NULL)
{
printf("The list is empty.\n");
return;
}
printf("Forward Traversal: ");
while (temp != NULL)
{
printf("%d->", temp->data);
temp = temp->next;
}
printf("NULL\n");
}
// Function to traverse the list in backward direction
void traverseBackward()
{
struct Node* temp = head;
if (temp == NULL)
{
printf("The list is empty.\n");
return;
}
// Traverse to the last node
while (temp->next != NULL)
{
temp = temp->next;
}
printf("Backward Traversal: ");
while (temp != NULL)
{
printf("%d->", temp->data);
temp = temp->prev;
}
printf("NULL\n");
}
// Function to free all allocated memory
void freeList()
{
struct Node* temp = head;
while (temp != NULL)
{
struct Node* nextNode = temp->next; // Save the next node
free(temp); // Free the current node
temp = nextNode; // Move to the next node
}
head = NULL; // Reset the head to NULL after freeing the list
}
// Main function to demonstrate the implementation
int main()
{
// Insert elements into the list
insertAtEnd(10);
insertAtEnd(20);
insertAtEnd(30);
insertAtEnd(40);
// Traverse the list in forward direction
traverseForward();
// Traverse the list in backward direction
traverseBackward();
// Free all allocated memory
freeList();
return 0;
}
Step-by-Step Execution
Initialization
Initially, the list is empty (head = NULL).
Insertion
Insert 10:
A new node with data = 10 is created.
Since the list is empty, this node becomes the head.
List: 10 -> NULL
Insert 20:
A new node with data = 20 is created.
Traverse to the last node (10) and link it to the new node.
List: 10 <-> 20 -> NULL
Insert 30:
A new node with data = 30 is created.
Traverse to the last node (20) and link it to the new node.
List: 10 <-> 20 <-> 30 -> NULL
Insert 40:
A new node with data = 40 is created.
Traverse to the last node (30) and link it to the new node.
List: 10 <-> 20 <-> 30 <-> 40 -> NULL
Forward Traversal
Start from the head (10) and traverse using the next pointer:
o Print: 10 -> 20 -> 30 -> 40 -> NULL
Backward Traversal
Traverse to the last node (40) using the next pointer.
Start from the last node (40) and traverse backward using the prev pointer:
o Print: 40 -> 30 -> 20 -> 10 -> NULL
Output
Forward Traversal: 10->20->30->40->NULL
Backward Traversal: 40->30->20->10->NULL
----------------------------------------------------------------------------------------
Algorithm for Deletions
//Header Files
#include <stdio.h>
#include <stdlib.h>
// Node structure for a doubly linked list
struct Node
{
int data;
struct Node* next;
struct Node* prev;
};
// Global head pointer
struct Node* head = NULL;
// Function to create a new node
struct Node* createNode(int data)
{
struct Node* newNode = malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
newNode->prev = NULL;
return newNode;
}
// 1. Delete from the beginning
void deleteFromBeginning()
{
if (head == NULL)
{
printf("List is empty. Nothing to delete.\n");
return;
}
struct Node* temp = head;
if (head->next == NULL)
{
// If there's only one node
head = NULL; // Set head to NULL
}
else
{
head = head->next; // Move head to the next node
head->prev = NULL; // Update the new head's prev pointer
}
free(temp); // Free the memory of the deleted node
printf("Deleted from the beginning.\n");
}
// 2. Delete from the end
void deleteFromEnd()
{
if (head == NULL)
{
printf("List is empty. Nothing to delete.\n");
return;
}
struct Node* temp = head;
while (temp->next != NULL)
{
// Traverse to the last node
temp = temp->next;
}
if (temp->prev == NULL)
{
// If there's only one node
head = NULL; // Set head to NULL
}
else
{
temp->prev->next = NULL; // Update the previous node's next pointer
}
free(temp); // Free the memory of the deleted node
printf("Deleted from the end.\n");
}
// 3. Delete a node by value (first occurrence)
void deleteByValue(int value)
{
if (head == NULL)
{
printf("List is empty. Nothing to delete.\n");
return;
}
struct Node* temp = head;
while (temp != NULL && temp->data != value)
{
// Traverse to find the node
temp = temp->next;
}
if (temp == NULL)
{
// Value not found
printf("Value %d not found in the list.\n", value);
return;
}
if (temp->prev == NULL)
{
// If the node to be deleted is the head
head = temp->next;
}
else
{
temp->prev->next = temp->next; // Update the previous node's next pointer
}
if (temp->next != NULL)
{
// If the node to be deleted is not the last node
temp->next->prev = temp->prev; // Update the next node's prev pointer
}
free(temp); // Free the memory of the deleted node
printf("Deleted node with value %d.\n", value);
}
// 4. Delete the entire list
void deleteEntireList()
{
struct Node* current = head;
while (current != NULL)
{
struct Node* temp = current;
current = current->next; // Move to the next node
free(temp); // Free the current node
}
head = NULL; // Set head to NULL
printf("Entire list deleted.\n");
}
// Utility function to print the list
void printList()
{
struct Node* temp = head;
if (temp == NULL)
{
printf("List is empty.\n");
return;
}
printf("List: ");
while (temp != NULL)
{
printf("%d ", temp->data);
temp = temp->next;
}
printf("\n");
}
// Main function to test the deletion operations
int main()
{
// Insert some nodes into the list
head = createNode(10);
head->next = createNode(27);
head->next->prev = head;
head->next->next = createNode(78);
head->next->next->prev = head->next;
head->next->next->next = createNode(45);
head->next->next->next->prev = head->next->next;
printf("Initial list:\n");
printList();
// Test deletion operations
printf("\nDeleting from the beginning...\n");
deleteFromBeginning();
printList();
printf("\nDeleting from the end...\n");
deleteFromEnd();
printList();
printf("\nDeleting node with value 20...\n");
deleteByValue(20);
printList();
printf("\nDeleting node with value 27...\n");
deleteByValue(27);
printList();
printf("\nDeleting the entire list...\n");
deleteEntireList();
printList();
return 0;
}
Output:
Initial list:
List: 10 27 78 45
Deleting from the beginning...
Deleted from the beginning.
List: 27 78 45
Deleting from the end...
Deleted from the end.
List: 27 78
Deleting node with value 20...
Value 20 not found in the list.
List: 27 78
Deleting node with value 27...
Deleted node with value 27.
List: 78
Deleting the entire list...
Entire list deleted.
List is empty.
Circular Linked List
A circular linked list is a variation of a singly linked list where the last node
points back to the first node (or head) instead of pointing to NULL.
This creates a circular structure, allowing traversal to continue indefinitely.
NODE Structure: Single Circular Linked List
DATA NEXT
Memory Location
NODE Structure: Double Circular Linked List
PREVIOUS DATA NEXT
Memory Location
Creating Circular Linked List
Structure of a Single Circular Linked List Node
struct Node {
int data;
struct Node* next;
};
// Global head pointer
struct Node* head = NULL;
In a circular linked list:
Each node contains two parts: data and a pointer to the next node .
The last node's next pointer points back to the first node (head), forming a
loop.
A global head pointer is used to keep track of the starting point of the list.