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

0% found this document useful (0 votes)
65 views34 pages

DS Unit 2

The document provides a comprehensive overview of linked lists, specifically focusing on singly linked lists, their representation, and various operations such as insertion, deletion, traversal, and searching. It includes code examples in C for implementing these operations, detailing how to manage nodes and pointers within the list. Additionally, it presents a complete program that allows users to interact with the linked list through a menu-driven interface.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
65 views34 pages

DS Unit 2

The document provides a comprehensive overview of linked lists, specifically focusing on singly linked lists, their representation, and various operations such as insertion, deletion, traversal, and searching. It includes code examples in C for implementing these operations, detailing how to manage nodes and pointers within the list. Additionally, it presents a complete program that allows users to interact with the linked list through a menu-driven interface.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 34

UNIT II

Linked Lists: Singly linked lists: representation and operations, doubly linked lists and circular
linked lists, Comparing arrays and linked lists, Applications of linked lists.
Singly linked lists
A singly linked list is a fundamental data structure that consists of a sequence of elements, where
each element points to the next one in the sequence in which the elements are not stored in
contiguous memory locations. Each element in the list is called a "node," and it contains two
components: the data (or payload) and a reference (or link) to the next node in the sequence. The
last node typically points to null, indicating the end of the list.

Representation of a single linked list


a. In-Memory Representation:
This describes how the linked list is stored in computer memory. Each node occupies a memory
location and consists of two parts:
 Data field: Stores the actual information (e.g., integer, string) associated with the node.
 Next pointer: Holds the memory address of the next node in the list. Since it's a single
link, each node points to only one other node.
Here's a visual representation:

b. Code Representation:
In programming languages, we use code structures to represent linked lists. Here's an example in
struct Node
{
int data; // Data field
struct Node* next; // Next pointer
};
This code defines a structure called Node that represents a single node in the linked list.
The data field stores the information, and the next pointer is a variable of type struct Node*,
meaning it can hold the address of another Node structure.
The head node is the first node in the list and has a next pointer that typically points to the
second node, or NULL if the list is empty. The end of the list is marked by a node with a next
pointer set to NULL. Traversing the list involves starting from the head and following the next
pointers until you reach a node with NULL in its next pointer.
Understanding both the in-memory representation and the code representation is essential
for effectively working with single linked lists.
List operations on linked list
In a linked list, each element (node) contains two parts: data and a reference (or pointer) to
the next node in the sequence. Here are the fundamental list operations commonly performed on a
linked list:
Insertion:
 Insert at the Beginning: Create a new node with the given data, set its next pointer to the
current head, and update the head to point to the new node.
 Insert at the End: Traverse the list until reaching the last node, then create a new node with
the given data and set the next pointer of the last node to point to the new node.
 Insert at a Specific Position: Traverse the list until reaching the desired position, then
perform the insertion similar to inserting at the beginning or end.
Deletion:
 Delete from the Beginning: Update the head to point to the next node and free the memory
allocated for the old head.
 Delete from the End: Traverse the list until reaching the second-to-last node, update its next
pointer to NULL, and free the memory allocated for the last node.
 Delete from a Specific Position: Traverse the list until reaching the node before the desired
position, update its next pointer to skip the desired node, and free the memory allocated for
the deleted node.
Traversal: Traverse the list from the head node to the end while printing or processing each node's
data.
Searching: Traverse the list from the head node to the end while comparing each node's data with
the target value until a match is found or the end of the list is reached.
Size/Length: Traverse the list while counting the number of nodes until reaching the end.
Insert at the Beginning linked list
Inserting a node at the beginning of a linked list involves creating a new node, setting its next
pointer to point to the current head of the list, and updating the head to point to the new node.
Here's how you can implement this operation in C:
void insertAtFront(int x)
{
struct Node* t;
t = (struct Node*)malloc(sizeof(struct Node));
t->data = x;
t->next = head;
head = t;
}

In this code:
We define a structure Node to represent each node of the linked list. It contains an integer data
field and a pointer next to the next node.
The function insertAtFront takes the data for the new node (x). It allocates memory for the new
node ‘t’, assigns the data, updates the next pointer to point to the current head, and updates the
head to point to the new node ‘t’.
Insert at the End of the linked list
To insert a node at the end of a linked list, you need to traverse the list until you reach the last
node, create a new node with the given data, and set the next pointer of the last node to point to
the new node. Here's how you can implement this operation in C:
void insertAtRear(int x)
{
struct Node* t, *temp;
t = (struct Node*)malloc(sizeof(struct Node));
temp=head;
t->data=x;
t->next=NULL;
if(head==NULL)
head=t;
else
{
while(temp->next!=NULL)
temp=temp->next;
temp->next=t;
}
}
In this code:
We define a structure Node to represent each node of the linked list. It contains an integer data
field and a pointer next to the next node.
The function insertAtRear takes the data for the new node (x). It allocates memory for the new
node, assigns the data, and sets its next pointer to NULL since it will be the last node. If the list is
empty, the new node becomes the head. Otherwise, it traverses the list until reaching the last node
and sets the next pointer of the last node to point to the new node.

Insert at the given position of the linked list


To insert a node at a given position in a linked list, you need to traverse the list until you reach the
node before the desired position, create a new node with the given data, and adjust the pointers to
insert the new node at the correct position. Here's how you can implement this operation in C:
void insertAtPosition(int x,int pos)
{
struct Node* t, *temp;
int i=1;
t = (struct Node*)malloc(sizeof(struct Node));
temp=head;
t->data=x;
if(head==NULL || pos==1)
insertAtFront(x);
else
{
while(i<pos-1&&temp->next!=NULL)
{
temp=temp->next;
i++;
}
t->next=temp->next;
temp->next=t;
}
}
In this code:

The function insertAtPosition takes the data for the new node (x), and the position where the new
node should be inserted (pos). If the position is 1 or the list is empty, the new node is inserted at
the beginning. Otherwise, it traverses the list until reaching the node before the desired position,
inserts the new node after that node, and adjusts the pointers accordingly. If the position is out of
range, it just inserts the element at the end of the linked list.
Delete the first element (head) of a linked list
To delete the first element (head) of a linked list, you need to update the head pointer to point to
the second node (if it exists) and free the memory allocated for the first node. Here's how you can
implement this operation in C:
void deleteAtFront()
{
struct Node *temp;
temp=head;
if(head==NULL)
printf("List is empty\n");
else
{
head=head->next;
printf("Deleted Element is %d\n",temp->data);
free(temp);
}
}
In this code:
The function deleteAtFront, checks if the list is empty. If the list is empty, the function just outputs
“List is empty”. If not, it stores a reference to the current head node (temp), updates the head to
point to the second node (if it exists), and frees the memory allocated for the old head node.
Delete element at rear in linked list
To delete the last element (rear) of a linked list, you need to traverse the list until you reach the
second-to-last node, update its next pointer to NULL, and free the memory allocated for the last
node. Here's how you can implement this operation in C:
void deleteAtRear()
{
struct Node *temp1, *temp2;
temp1=head;
if(head==NULL)
printf("List is empty\n");
else
{
if(head->next==NULL)
deleteAtFront();
else
{
while(temp1->next!=NULL)
{
temp2=temp1;
temp1=temp1->next;
}
temp2->next=NULL;
printf("Deleted Element is %d\n",temp1->data);
free(temp1);
}

}
}
In this code:

The function deleteAtRear, checks if the list is empty or if there is only one node, or more than
one node. If the list is empty, the function just outputs “List is empty”. If not, it traverses the list
until reaching the second-to-last node, frees the memory allocated for the last node, and updates
the next pointer of the second-to-last node to NULL.
Delete element positionally in linked list
To delete an element at a given position in a linked list, you need to traverse the list until you reach
the node before the desired position, adjust the pointers to skip the node to be deleted, and free the
memory allocated for that node. Here's how you can implement this operation in C:
void deleteAtPosition(int pos)
{
struct Node *temp1, *temp2;
int i=1;
temp1=head;
if(head==NULL)
printf("List is empty\n");
else
{
while(i<pos&&temp1!=NULL)
{
temp2=temp1;
temp1=temp1->next;
i++;
}
if(temp1!=NULL)
{
temp2->next=temp1->next;
printf("Deleted Element is %d\n",temp1->data);
free(temp1);
}
}
}
The deleteAtPosition function takes the position of the node to be deleted. It handles three cases:
deleting the head node if the given position is 1, deleting a node in the middle of the list, and it
will not delete any node if the input is out of range.

The complete program of all operations in single linked list is:


#include <stdio.h>
#include <stdlib.h>
// Define a structure for a node in the linked list
struct Node {
int data; // Data of the node
struct Node* next; // Pointer to the next node in the list
};
struct Node* head = NULL;
// Function to insert a new node at the beginning of the linked list
void insertAtFront(int x) {
struct Node* t;
t = (struct Node*)malloc(sizeof(struct Node));
t->data = x;
t->next = head;
head = t;
}
void insertAtRear(int x)
{
struct Node* t, *temp;
t = (struct Node*)malloc(sizeof(struct Node));
temp=head;
t->data=x;
t->next=NULL;
if(head==NULL)
head=t;
else
{
while(temp->next!=NULL)
temp=temp->next;
temp->next=t;
}
}
void insertAtPosition(int x,int pos)
{
struct Node* t, *temp;
int i=1;
temp=head;
if(head==NULL || pos==1)
insertAtFront(x);
else
{
t = (struct Node*)malloc(sizeof(struct Node));
t->data=x;
while(i<pos-1&&temp->next!=NULL)
{
temp=temp->next;
i++;
}
t->next=temp->next;
temp->next=t;
}
}
void deleteAtFront()
{
struct Node *temp;
temp=head;
if(head==NULL)
printf("List is empty\n");
else
{
head=head->next;
printf("Deleted Element is %d\n",temp->data);
free(temp);
}
}
void deleteAtRear()
{
struct Node *temp1, *temp2;
temp1=head;
if(head==NULL)
printf("List is empty\n");
else
{
if(head->next==NULL)
deleteAtFront();
else
{
while(temp1->next!=NULL)
{
temp2=temp1;
temp1=temp1->next;
}
temp2->next=NULL;
printf("Deleted Element is %d\n",temp1->data);
free(temp1);
}

}
}
void deleteAtPosition(int pos)
{
struct Node *temp1, *temp2;
int i=1;
temp1=head;
if(head==NULL)
printf("List is empty\n");
else
{
while(i<pos&&temp1!=NULL)
{
temp2=temp1;
temp1=temp1->next;
i++;
}
if(temp1!=NULL)
{
temp2->next=temp1->next;
printf("Deleted Element is %d\n",temp1->data);
free(temp1);
}
}
}
int search(int x)
{
struct Node* t = head;
int i=1;
while (t!= NULL)
{
if(t->data==x)
return i;
t= t->next;
i++;
}
return(-1);
}
// Function to print the linked list
void printList()
{
struct Node* t = head;
while (t!= NULL)
{
printf("%d -> ", t->data);
t= t->next;
}
printf("NULL\n");
}

int main()
{
int ch,x,pos;
while(1)
{
printf("\n1.Insert Front\n2.Insert Rear\n3.Insert Positionally\n4.Delete Front \n5.Delete
Rear\n");
printf("6. Delete Positionally\n7. Search\n8.PrintList\n9.Exit\n");
printf("Enter your choice");
scanf("%d",&ch);
switch(ch)
{
case 1: printf("Enter element to insert");
scanf("%d",&x);
insertAtFront(x);
printList();
break;
case 2: printf("Enter element to insert");
scanf("%d",&x);
insertAtRear(x);
printList();
break;
case 3: printf("Enter element to insert and position to insert");
scanf("%d%d",&x,&pos);
insertAtPosition(x,pos);
printList();
break;
case 4: deleteAtFront();
printList();
break;
case 5: deleteAtRear();
printList();
break;
case 6: printf("Enter position to delete");
scanf("%d",&pos);
deleteAtPosition(pos);
printList();
break;
case 7:printf("Enter element to search");
scanf("%d",&x);
pos=search(x);
if(pos==-1)
printf("Element not fount\n");
else
printf("Found at %d position\n",pos);
case 8:printList();
break;
case 9: exit(0);
}
}
return 0;
}

OUTPUT

1.Insert Front
2.Insert Rear
3.Insert Positionally
4.Delete Front
5.Delete Rear
6. Delete Positionally
7. Search
8.PrintList
9.Exit
Enter your choice1
Enter element to insert20
20 -> NULL

1.Insert Front
2.Insert Rear
3.Insert Positionally
4.Delete Front
5.Delete Rear
6. Delete Positionally
7. Search
8.PrintList
9.Exit
Enter your choice2
Enter element to insert10
20 -> 10 -> NULL

1.Insert Front
2.Insert Rear
3.Insert Positionally
4.Delete Front
5.Delete Rear
6. Delete Positionally
7. Search
8.PrintList
9.Exit
Enter your choice3
Enter element to insert and position to insert25
2
20 -> 25 -> 10 -> NULL

1.Insert Front
2.Insert Rear
3.Insert Positionally
4.Delete Front
5.Delete Rear
6. Delete Positionally
7. Search
8.PrintList
9.Exit
Enter your choice7
Enter element to search10
Found at 3 position
20 -> 25 -> 10 -> NULL

1.Insert Front
2.Insert Rear
3.Insert Positionally
4.Delete Front
5.Delete Rear
6. Delete Positionally
7. Search
8.PrintList
9.Exit
Enter your choice6
Enter position to delete2
Deleted Element is 25
20 -> 10 -> NULL

1.Insert Front
2.Insert Rear
3.Insert Positionally
4.Delete Front
5.Delete Rear
6. Delete Positionally
7. Search
8.PrintList
9.Exit
Enter your choice5
Deleted Element is 10
20 -> NULL

1.Insert Front
2.Insert Rear
3.Insert Positionally
4.Delete Front
5.Delete Rear
6. Delete Positionally
7. Search
8.PrintList
9.Exit
Enter your choice4
Deleted Element is 20
NULL

1.Insert Front
2.Insert Rear
3.Insert Positionally
4.Delete Front
5.Delete Rear
6. Delete Positionally
7. Search
8.PrintList
9.Exit
Enter your choice9

Process returned 0 (0x0) execution time : 111.201 s


Press any key to continue.
Doubly linked list (DLL)
A doubly linked list (DLL) is a data structure that consists of a sequence of elements called
nodes. Unlike a singly linked list, where each node points to the next node in the sequence, a
doubly linked list has nodes with two pointers: one pointing to the next node and another pointing
to the previous node. This enables bidirectional traversal of the list.
Representation of a double linked list
In-Memory Representation:
This describes how the linked list is stored in computer memory. Each node occupies a memory
location and consists of three parts:
 Data field: Stores the actual information (e.g., integer, string) associated with the node.
 rlink pointer: Holds the memory address of the next node in the list.
 llink pointer: Holds the memory address of the previous node in the list.
Here's a visual representation:
Double Linked List Node Properties

llink rlink
(Holds a address (Holds a address
to the previous
Data to the previous
node) node)

Code Representation:
In programming languages, we use code structures to represent linked lists. Here's an example in
struct Node
{
int data;
struct Node* rlink;
struct Node* llink;
};
This code defines a structure called Node that represents a single node in the linked list.
The data field stores the information, and the rlink & llink pointer is a variable of type struct
Node*, meaning it can hold the address of the next and previous Node of the linked list.
Advantages of Doubly Linked Lists:
Doubly linked lists (DLLs) offer several advantages over singly linked lists due to their
bidirectional nature. Here are some of the key advantages:
Bidirectional Traversal: In DLLs, each node has pointers to both its previous and next nodes.
This allows for efficient traversal in both forward and backward directions. Singly-linked lists only
support forward traversal, requiring additional time and space to traverse in reverse.
Insertion and Deletion Operations: Insertion and deletion operations at the beginning and end
of the list are more efficient in DLLs compared to singly linked lists. This is because DLLs do not
require traversal from the head or tail to perform these operations. In contrast, singly linked lists
often require traversal to the insertion or deletion point, which can be inefficient.
Reverse Traversal: DLLs support efficient reverse traversal, which can be useful in various
scenarios such as printing data in reverse order or performing operations from the end of the list
to the beginning.
Memory Efficiency for Some Operations: While DLLs have an additional pointer per node
compared to singly linked lists, they can be more memory-efficient for certain operations. For
example, reversing a singly linked list requires additional space for stack or recursive calls, while
reversing a DLL can be done in place without extra memory.
Efficient Implementation of Algorithms: Certain algorithms, such as merge sort and quicksort,
benefit from bidirectional traversal offered by DLLs. These algorithms can efficiently manipulate
DLLs for sorting operations, improving overall performance.
Circular Lists: DLLs can easily be made circular by connecting the last node's next pointer to the
first node and the first node's previous pointer to the last node. This circular structure facilitates
operations such as rotation and can be used in applications like circular buffers and queues.
Overall, DLLs provide enhanced flexibility and efficiency for various operations compared
to singly linked lists, especially when bidirectional traversal and frequent insertion/deletion
operations are required. However, it's essential to consider the additional memory overhead
associated with maintaining the prev pointers in DLLs.
Operations on Doubly Linked Lists:
Insertion:
Insert at Beginning: Add a new node at the beginning of the list.
Insert at End: Append a new node at the end of the list.
Insert at Position: Insert a new node at a specific position in the list.
Deletion:
Delete from Beginning: Remove the first node from the list.
Delete from End: Remove the last node from the list.
Delete at Position: Remove a node from a specific position in the list.
Traversal: Traverse the list either forward or backward using the next and previous pointers of
each node.
Searching: Search for a specific value within the list by traversing it from the head or tail.
Operations on Doubly Linked Lists:
Insert at Beginning of the double linked list:
Inserting a node at the beginning of a doubly linked list involves creating a new node,
adjusting pointers to make it the new head, and updating the previous pointer of the old head (if it
exists) to point to the new node.

 Let us assume a newNode as shown above. The newNode with data = 25 has to
be inserted at the beginning of the list.
 The rlink pointer of the newNode is referenced to the head node and its llink
pointer is referenced to NULL.
 The llink pointer of the head node is referenced to the newNode.
 The newNode is then made as the head node.

void insertAtFront(int x)
{
struct Node* t;
t = (struct Node*)malloc(sizeof(struct Node));
t->data = x;
t->llink=NULL;
if(head!=NULL)
head->llink=t;
t->rlink=head;
head = t;
}
In this code:
We define a structure Node to represent each node of the doubly linked list. It contains an
integer data field and two pointers: rlink points to the next node, and llink points to the previous
node. The malloc() function is used to create a new node with the given data. The
insertAtBeginning function takes the data for the new node. It creates a new node and inserts it at
the beginning of the list by adjusting pointers appropriately.
Insert at End of the double linked list:
To insert a node at the end of a doubly linked list, you need to traverse the list until you
reach the last node, then adjust pointers to make the new node the new last node. If the list is empty,
the new node becomes the head of the list.

Now, let us assume a newNode as shown above. The newNode with data = 25 has to be
inserted at the end of the linked list. Make the rlink pointer of the last node to point to
the newNode. The rlink pointer of the newNode is referenced to NULL and its llink pointer
is made to point to the last node. Then, the newNode is made as the last node.

Here's how you can implement this operation in C:


void insertAtRear(int x)
{
struct Node* t, *temp;
t = (struct Node*)malloc(sizeof(struct Node));
temp=head;
t->data=x;
t->rlink=NULL;
if(head==NULL)
{
t->llink=NULL;
head=t;
}
else
{
while(temp->rlink!=NULL)
temp=temp->rlink;
t->llink=temp;
temp->rlink=t;
}
}

In this code:
We define a structure Node to represent each node of the doubly linked list. It contains an integer
data field and two pointers: rlink points to the next node, and llink points to the previous node. The
insertAtEnd function takes the data for the new node. It creates a new node and inserts it at the end
of the list by traversing the list until the last node is found, then adjusting pointers accordingly.
Insert at given position in the double linked list:
To insert a node at a given position in a doubly linked list, you need to traverse the list to find the
node at the desired position, then adjust pointers to insert the new node before or after it.
Now let us assume that a newNode with data = 25 has to be inserted at position 2 in the
linked list. Start traversing the list from head and move upto the position - 1. In this case, since we
want to insert at second position, you need to traverse till (2 - 1) = 1 st node. Once you reach the
position - 1 node, the next pointer of the newNode is set to the address contained by the next
pointer of the (position - 1) th node. Make the rlink pointer of the (position - 1) th node to point to
the newNode and the llink pointer of the newNode is pointed to the (position - 1) th node. Finally
the newNode next 's llink pointer must be made to point to the newNode.
Here's how you can implement this operation in C:
void insertAtPosition(int x,int pos)
{
struct Node *t, *temp, *temp2;
int c=1;
if(head==NULL || pos==1)
insertAtFront(x);
else
{
t = (struct Node*)malloc(sizeof(struct Node));
temp=head;
t->data=x;
while(c<pos-1 && temp->rlink!=NULL)
{
temp=temp->rlink;
c++;
}
if(temp->rlink==NULL)
insertAtRear(x);
else
{
temp2=temp->rlink;
t->rlink=temp2;
t->llink=temp;
temp2->llink=t;
temp->rlink=t;
}
}
}

Delete an element Beginning of the double linked list


Deleting an element from the beginning of a doubly linked list is a straightforward process due to
the presence of both forward and backward pointers. Here's how it works:
Steps:
 Check for empty list: If the head pointer (head) is null, the list is empty. There's nothing to
delete, so you can simply return or exit the function.
 Store the head node: Create a temporary pointer (temp) and assign it the value of the current
head node (head). This is because you'll be updating the head pointer later, and you might
need the data of the node being deleted.
 Update the head pointer: Move the head pointer to the next node in the list. You can achieve
this by assigning head to head->rlink.
 Handle the new head's previous pointer (if applicable):
o If the list has only one element (meaning head->rlink is also null), set the head to
null after step 3. This effectively removes the only element in the list.
o Otherwise, update the llink pointer of the new head node (head->llink). Set it to
null since it becomes the first node.
 Delete the old head node: If your programming language requires explicit memory
management, you might need to deallocate the memory occupied by the temp node. This
typically involves using a free function.
Here's how you can implement this operation in C:
void deleteAtFront()
{
struct Node *temp;
temp=head;
if(head==NULL)
printf("List is empty\n");
else
{
if(head->rlink==NULL)
head=NULL;
else
{
head=head->rlink;
head->llink=NULL;
}
printf("Deleted Element is %d\n",temp->data);
free(temp);
}
}
Delete an element at end of the double linked list
Deleting an element from the end of a doubly linked list also utilizes the two-way pointers for
efficient manipulation. Here's how it works:
Steps:
1. Check for empty list: If the head pointer (head) is null, the list is empty. There's nothing to
delete, so you can simply return or exit the function.
2. Handle single element list: if the list contains only one element i.e head->rlink is null, set
the head to null. This effectively removes the only element in the list.
3. Traverse to the second last node: Use a temporary pointer (temp1) to iterate through the
list. Move it until temp1->rlink is null. This temp1 will point to the last node. Create temp2
with temp1->llink. Now temp2 is the second last node in the list.
4. Update the second last node's rlink pointer: Set temp->next (the second last node) to point
to null. This effectively removes the last node from the list.
5. Delete the last node: Similar to deleting from the beginning, if your language requires
explicit memory management, you might need to deallocate the memory occupied by the
tail node using a free or delete function.

void deleteAtRear()
{
struct Node *temp1, *temp2;
temp1=head;
if(head==NULL)
printf("List is empty\n");
else
{
if(head->rlink==NULL)
head=NULL;
else
{
while(temp1->rlink!=NULL)
temp1=temp1->rlink;
temp2=temp1->llink;
temp2->rlink=NULL;
printf("Deleted Element is %d\n",temp1->data);
}
free(temp1);
}
}

Delete an element at given position of the double linked list


Deleting an element at a given position in a doubly linked list requires a bit more work
compared to removing from the beginning or end. Here's how it goes:
Steps:
1. Check for empty list: If the head pointer (head) is null, the list is empty. There's nothing
to delete, so you can simply return or exit the function.
2. Handle deleting the head node (position 1): If pos is 1, it means you want to delete the
head node. This is a special case you can handle using the deletefront() function.
3. Traverse to the node before deletion position: Use a temporary pointer (temp1) to
iterate through the list. Move temp pos - 1 times to reach the node right before the one
you want to delete.
4. Handle deleting the last node: If temp1->next->next is null (i.e., temp points to the
second last node), it means you're deleting the last node. This can be handled similarly
to deleting from the end (refer to the previous explanation).
5. Store the node to be deleted: Create another temporary pointer (temp2) and assign it
temp1->rlink (the node you want to delete).
6. Update pointers around the deleted node:
a. Set temp1->rlink to point to temp2->rlink (the node after the one you're
deleting).
b. If temp2->next is not null (i.e., not the last node), update its llink pointer to
point to temp (the node before the deleted one).
7. Delete the node (optional): As mentioned before, you might need to explicitly
deallocate the memory occupied by the del node using a free or delete function.
1. void deleteAtPosition(int pos)
2. {
3. struct Node *temp1, *temp2;
4. int c=1;
5. temp1=head;
6. if(head==NULL)
7. printf("List is empty\n");
8. else
9. {
10. if (pos==1)
11. deleteAtFront();
12. else
13. {
14. while(c<pos-1&&temp1->rlink!=NULL)
15. {
16. temp1=temp1->rlink;
17. c++;
18. }
19. if(temp1->rlink!=NULL)
20. {
21. temp2=temp1->rlink;
22. temp1->rlink=temp2->rlink;
23. temp2->rlink->llink=temp1;
24. printf("Deleted Element is %d\n",temp2->data);
25. free(temp2);
26. }
27. }
28. }
29. }

#include <stdio.h>
#include <stdlib.h>
// Define a structure for a node in the Double Linked List
struct Node {
int data; // Data of the node
struct Node* llink;
struct Node* rlink; // Pointer to the next node in the list
};
struct Node* head = NULL;
// Function to insert a new node at the beginning of the linked list
void insertAtFront(int x)
{
struct Node* t;
t = (struct Node*)malloc(sizeof(struct Node));
t->data = x;
t->llink=NULL;
if(head!=NULL)
head->llink=t;
t->rlink=head;
head = t;
}
void insertAtRear(int x)
{
struct Node* t, *temp;
t = (struct Node*)malloc(sizeof(struct Node));
temp=head;
t->data=x;
t->rlink=NULL;
if(head==NULL)
{
t->llink=NULL;
head=t;
}
else
{
while(temp->rlink!=NULL)
temp=temp->rlink;
t->llink=temp;
temp->rlink=t;
}

}
void insertAtPosition(int x,int pos)
{
struct Node *t, *temp, *temp2;
int c=1;
if(head==NULL || pos==1)
insertAtFront(x);
else
{
t = (struct Node*)malloc(sizeof(struct Node));
temp=head;
t->data=x;
while(c<pos-1 && temp->rlink!=NULL)
{
temp=temp->rlink;
c++;
}
if(temp->rlink==NULL)
insertAtRear(x);
else
{
temp2=temp->rlink;
t->rlink=temp2;
t->llink=temp;
temp2->llink=t;
temp->rlink=t;
}
}
}
void deleteAtFront()
{
struct Node *temp;
temp=head;
if(head==NULL)
printf("List is empty\n");
else
{
if(head->rlink==NULL)
head=NULL;
else
{
head=head->rlink;
head->llink=NULL;
}
printf("Deleted Element is %d\n",temp->data);
free(temp);
}
}
void deleteAtRear()
{
struct Node *temp1, *temp2;
temp1=head;
if(head==NULL)
printf("List is empty\n");
else
{
if(head->rlink==NULL)
head=NULL;
else
{
while(temp1->rlink!=NULL)
temp1=temp1->rlink;
temp2=temp1->llink;
temp2->rlink=NULL;
printf("Deleted Element is %d\n",temp1->data);
}
free(temp1);
}
}
void deleteAtPosition(int pos)
{
struct Node *temp1, *temp2;
int c=1;
temp1=head;
if(head==NULL)
printf("List is empty\n");
else
{
if (pos==1)
deleteAtFront();
else
{
while(c<pos-1&&temp1->rlink!=NULL)
{
temp1=temp1->rlink;
c++;
}
if(temp1->rlink!=NULL)
{
temp2=temp1->rlink;
temp1->rlink=temp2->rlink;
temp2->rlink->llink=temp1;
printf("Deleted Element is %d\n",temp2->data);
free(temp2);
}
}
}
}
int search(int x)
{
struct Node* t = head;
int c=1;
while (t!= NULL)
{
if(t->data==x)
return c;
t= t->rlink;
c++;
}
return(-1);
}
// Function to print the linked list
void printListForward()
{
struct Node* t = head;
while (t!= NULL)
{
printf("%d -> ", t->data);
t= t->rlink;
}
printf("NULL\n");
}
void printListBackward()
{
struct Node* t = head;
while (t->rlink!= NULL)
t= t->rlink;
while(t!=NULL)
{
printf("%d -> ", t->data);
t= t->llink;
}
printf("NULL\n");
}
int main()
{
int ch,x,pos;
while(1)
{
printf("\n1.Insert Front\n2.Insert Rear\n3.Insert Positionally\n4.Delete Front \n5.Delete
Rear\n");
printf("6. Delete Positionally\n7.
Search\n8.PrintListForward\n9.PrintListBackward\n10.Exit\n");
printf("Enter your choice");
scanf("%d",&ch);
switch(ch)
{
case 1: printf("Enter element to insert");
scanf("%d",&x);
insertAtFront(x);
printListForward();
break;
case 2: printf("Enter element to insert");
scanf("%d",&x);
insertAtRear(x);
printListForward();
break;
case 3: printf("Enter element to insert and position to insert");
scanf("%d%d",&x,&pos);
insertAtPosition(x,pos);
printListForward();
break;
case 4: deleteAtFront();
printListForward();
break;
case 5: deleteAtRear();
printListForward();
break;
case 6: printf("Enter position to delete");
scanf("%d",&pos);
deleteAtPosition(pos);
printListForward();
break;
case 7:printf("Enter element to search");
scanf("%d",&x);
pos=search(x);
if(pos==-1)
printf("Element not fount\n");
else
printf("Found at %d position\n",pos);
case 8:printListForward();
break;
case 9:printf("\n Backward List Traversing\n");
printListBackward();
break;
case 10: exit(0);
}
}
return 0;
}
Comparing Arrays and Linked Lists
Arrays and linked lists are fundamental data structures used to store collections of elements. Both
have distinct advantages and disadvantages based on their structure, performance, and use cases.
Below are detailed notes comparing these two data structures across various aspects:
1. Structure
 Arrays:
o Fixed Size: Arrays have a fixed size that must be defined at the time of creation.
o Contiguous Memory: Elements are stored in contiguous memory locations.
o Indexing: Direct indexing allows O(1) time complexity for accessing elements.
 Linked Lists:
o Dynamic Size: Linked lists can grow and shrink dynamically.
o Non-contiguous Memory: Elements (nodes) are stored in non-contiguous memory
locations, with each node containing a reference (or pointer) to the next node.
o No Direct Indexing: Accessing elements requires traversal from the head, resulting
in O(n) time complexity for accessing elements.
2. Insertion and Deletion
 Arrays:
o Insertion: Inserting an element at a specific position requires shifting elements to
the right, resulting in O(n) time complexity in the worst case.
o Deletion: Deleting an element requires shifting elements to the left, also resulting
in O(n) time complexity in the worst case.
 Linked Lists:
o Insertion: Inserting an element at the beginning (or end, if tail pointer is
maintained) can be done in O(1) time. Insertion at a specific position requires
traversal, resulting in O(n) time complexity.
o Deletion: Deleting an element (given a reference to the node) can be done in O(1)
time. However, finding the node to delete requires O(n) time complexity in the
worst case.
3. Memory Usage
 Arrays:
o Static Allocation: Memory is allocated at the time of array creation. This can lead
to either wasted space (if the array is too large) or insufficient space (if the array is
too small).
o Overhead: Minimal overhead since only the elements are stored.
 Linked Lists:
o Dynamic Allocation: Memory is allocated as needed. This is efficient in terms of
space utilization but can lead to overhead due to storing pointers (or references).
o Overhead: Each node requires additional memory for storing the pointer to the
next node (and previous node in the case of doubly linked lists).
4. Traversal
 Arrays:
o Direct Access: Elements can be accessed directly using indices, allowing O(1) time
complexity for both read and write operations.
 Linked Lists:
o Sequential Access: Elements must be accessed sequentially starting from the head
node, resulting in O(n) time complexity for traversal.
5. Flexibility
 Arrays:
o Size Constraint: The size of an array is fixed after its creation. Resizing an array
involves creating a new array and copying elements, which is costly.
 Linked Lists:
o Dynamic Size: Linked lists can easily grow and shrink in size, providing more
flexibility in handling varying amounts of data.
7. Complexity Summary
Operation Arrays Linked Lists

Access by Index O(1) O(n)

Insertion at Beginning O(n) O(1)

Insertion at End O(1) or O(n)* O(1) (if tail pointer)

Deletion at Beginning O(n) O(1)

Deletion at End O(1) or O(n)* O(n) or O(1)**

Searching O(n) O(n)

Arrays and linked lists each have their strengths and weaknesses. The choice between them
depends on the specific requirements of the application, such as the need for dynamic resizing,
access speed, and memory usage. Understanding these differences is crucial for selecting the
appropriate data structure for a given problem.
Applications of Linked Lists
Linked lists are a fundamental data structure with a wide range of applications in various areas of
computer science and programming. Their flexibility and dynamic nature make them suitable for
scenarios where arrays might not be the best fit. Below are detailed notes on the applications of
linked lists:
1. Implementation of Abstract Data Types (ADTs)
 Stacks: Linked lists can be used to implement stacks. The push and pop operations can be
efficiently implemented by inserting and deleting nodes at the head of the list.
 Queues: Linked lists are ideal for implementing queues. By maintaining pointers to both
the head and the tail, enqueue and dequeue operations can be performed in O(1) time.
 Deques (Double-Ended Queues): Linked lists support efficient insertion and deletion at
both ends, making them suitable for implementing deques.
2. Dynamic Memory Allocation
 Flexible Size: Linked lists are used in dynamic memory allocation schemes where the size
of the data structure changes frequently. Unlike arrays, linked lists do not require pre-
allocation of memory.
 Memory Pools: They can be used to manage memory pools where blocks of memory need
to be allocated and freed dynamically.
3. Graph Representation
 Adjacency Lists: Linked lists are commonly used to represent graphs. Each vertex in the
graph can be associated with a linked list that contains all the adjacent vertices. This
representation is efficient in terms of space and is suitable for sparse graphs.
4. Real-Time Applications
 Music Player Playlists: Linked lists are used to manage playlists in music players where
songs can be dynamically added, removed, or moved.
 Image Viewer: In image viewers, linked lists can be used to navigate through a series of
images, allowing the user to go forward and backward.
5. Operating Systems
 Process Scheduling: Linked lists are used in operating systems for scheduling processes.
Ready queues and other scheduling structures can be efficiently implemented using linked
lists.
 Memory Management: Free memory blocks can be managed using linked lists. This helps
in efficiently allocating and deallocating memory blocks.
6. Undo Functionality
 Text Editors: Linked lists are used to implement undo functionality in text editors. Each
change can be represented as a node in the list, allowing the editor to traverse back through
the list to undo changes.
7. Hash Tables with Separate Chaining
 Collision Handling: Linked lists are used in hash tables to handle collisions via separate
chaining. Each bucket of the hash table contains a linked list to store all elements that hash
to the same index.

You might also like