Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
11 views58 pages

DS Lab Notes - Unit 2

This document covers the fundamentals of linked lists in C programming, including their types (single, double, and circular linked lists) and memory allocation methods. It highlights the differences between arrays and linked lists, emphasizing the advantages and disadvantages of linked lists, such as dynamic sizing and efficient insertion/deletion. Additionally, it provides algorithms and code snippets for creating and manipulating linked lists, including insertion and traversal operations.

Uploaded by

anushya.himate
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views58 pages

DS Lab Notes - Unit 2

This document covers the fundamentals of linked lists in C programming, including their types (single, double, and circular linked lists) and memory allocation methods. It highlights the differences between arrays and linked lists, emphasizing the advantages and disadvantages of linked lists, such as dynamic sizing and efficient insertion/deletion. Additionally, it provides algorithms and code snippets for creating and manipulating linked lists, including insertion and traversal operations.

Uploaded by

anushya.himate
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 58

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.

You might also like