Singly Linked List
A singly linked list is a type of linked list where each node has just one link pointing to the next node.
Linked List Operations: Traverse, Insert and Delete
There are different linked list operations that let us do various tasks on linked lists. For example, the insertion operation adds a new element to the linked list.
Here's a list of basic linked list operations that we will cover in this article:
Traversal - access each element of the linked list
Insertion - add a new element to the linked list
Deletion - remove existing elements
Search - find a node in the linked list
Sort - sort the nodes of the linked list
Things to Remember about Linked List
head points to the first node of the linked list
next pointer of the last node is NULL, so if the next current node is NULL, we have reached the end of the linked list.
In all of the examples, we will assume that the linked list has three nodes 1 --->2 --->3
with node structure as below:
struct node {
int data;
struct node *next;
};
Traverse a Linked List
Displaying the contents of a linked list is very simple. We keep moving the temp node to the next one and display its contents.
When temp is NULL, we know we have reached the end of the linked list, so we exit the while loop.
struct node *temp = head;
printf("\n\nList elements are - \n");
while(temp != NULL) {
printf("%d --->",temp->data);
temp = temp->next;
}
The output of this program will be:
List elements are -
1 --->2 --->3 --->
Insert Elements to a Linked List
You can add elements to either the beginning, middle or end of the linked list.
1. Insert at the beginning
Allocate memory for new node
Store data
Change next of new node to point to head
Change head to point to recently created node
struct node *newNode;
newNode = malloc(sizeof(struct node));
newNode->data = 4;
newNode->next = head;
head = newNode;
2. Insert at the End
Allocate memory for new node
Store data
Traverse to last node
Change next of last node to recently created node
struct node *newNode;
newNode = malloc(sizeof(struct node));
newNode->data = 4;
newNode->next = NULL;
struct node *temp = head;
while(temp->next != NULL){
temp = temp->next;
}
temp->next = newNode;
3. Insert at the Middle
Allocate memory and store data for the new node
Move to the node just before where you want to insert the new node
Update the next pointers to insert the new node in between
struct node *newNode;
newNode = malloc(sizeof(struct node));
newNode->data = 4;
struct node *temp = head;
for(int i=2; i < position; i++) {
if(temp->next != NULL) {
temp = temp->next;
}
}
newNode->next = temp->next;
temp->next = newNode;
Delete from a Linked List
You can delete either from the beginning, end or from a particular position.
1. Delete from beginning
- Point head to the second node
head = head->next;
2. Delete from end
Traverse to second last element
Change its next pointer to null
struct node* temp = head;
while(temp->next->next!=NULL){
temp = temp->next;
}
temp->next = NULL;
3. Delete from middle
Traverse to the node before the one you want to delete
Change the next pointers to skip the node you want to remove
for(int i=2; i< position; i++) {
if(temp->next!=NULL) {
temp = temp->next;
}
}
temp->next = temp->next->next;
Search an Element on a Linked List
YYou can search for an element in a linked list using a loop with these steps. We are looking for item
in the linked list.
Set
head
as thecurrent
node.Run a loop until the
current
node isNULL
because the last element points toNULL
.In each loop, check if the node's key is equal to
item
. If it matches, returntrue
; otherwise, returnfalse
.
// Search a node
bool searchNode(struct Node** head_ref, int key) {
struct Node* current = *head_ref;
while (current != NULL) {
if (current->data == key) return true;
current = current->next;
}
return false;
}
Sort Elements of a Linked List
We will use a simple sorting algorithm, Bubble Sort, to sort the elements of a linked list in ascending order.
Make the
head
thecurrent
node and create another nodeindex
for later use.If
head
is null, return.Otherwise, run a loop until the last node (i.e.,
NULL
).In each iteration, follow steps 5-6.
Store the next node of
current
inindex
.Check if the data of the current node is greater than the next node. If it is, swap
current
andindex
.
// Sort the linked list
void sortLinkedList(struct Node** head_ref) {
struct Node *current = *head_ref, *index = NULL;
int temp;
if (head_ref == NULL) {
return;
} else {
while (current != NULL) {
// index points to the node next to current
index = current->next;
while (index != NULL) {
if (current->data > index->data) {
temp = current->data;
current->data = index->data;
index->data = temp;
}
index = index->next;
}
current = current->next;
}
}
}
LinkedList Operations in Python, Java, C, and C++
# Linked list operations in Python
# Create a node
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
# Insert at the beginning
def insertAtBeginning(self, new_data):
new_node = Node(new_data)
new_node.next = self.head
self.head = new_node
# Insert after a node
def insertAfter(self, prev_node, new_data):
if prev_node is None:
print("The given previous node must inLinkedList.")
return
new_node = Node(new_data)
new_node.next = prev_node.next
prev_node.next = new_node
# Insert at the end
def insertAtEnd(self, new_data):
new_node = Node(new_data)
if self.head is None:
self.head = new_node
return
last = self.head
while (last.next):
last = last.next
last.next = new_node
# Deleting a node
def deleteNode(self, position):
if self.head is None:
return
temp = self.head
if position == 0:
self.head = temp.next
temp = None
return
# Find the key to be deleted
for i in range(position - 1):
temp = temp.next
if temp is None:
break
# If the key is not present
if temp is None:
return
if temp.next is None:
return
next = temp.next.next
temp.next = None
temp.next = next
# Search an element
def search(self, key):
current = self.head
while current is not None:
if current.data == key:
return True
current = current.next
return False
# Sort the linked list
def sortLinkedList(self, head):
current = head
index = Node(None)
if head is None:
return
else:
while current is not None:
# index points to the node next to current
index = current.next
while index is not None:
if current.data > index.data:
current.data, index.data = index.data, current.data
index = index.next
current = current.next
# Print the linked list
def printList(self):
temp = self.head
while (temp):
print(str(temp.data) + " ", end="")
temp = temp.next
if __name__ == '__main__':
llist = LinkedList()
llist.insertAtEnd(1)
llist.insertAtBeginning(2)
llist.insertAtBeginning(3)
llist.insertAtEnd(4)
llist.insertAfter(llist.head.next, 5)
print('linked list:')
llist.printList()
print("\nAfter deleting an element:")
llist.deleteNode(3)
llist.printList()
print()
item_to_find = 3
if llist.search(item_to_find):
print(str(item_to_find) + " is found")
else:
print(str(item_to_find) + " is not found")
llist.sortLinkedList(llist.head)
print("Sorted List: ")
llist.printList()
Complexity
Data Structure | Time Complexity | Space Compleity | |||||||
Average | Worst | Worst | |||||||
Access | Search | Insertion | Deletion | Access | Search | Insertion | Deletion | ||
Singly Linked List | θ(n) | θ(n) | θ(1) | θ(1) | O(n) | O(n) | O(1) | O(1) | O(n) |
Characteristics of a Singly Linked List:
Each node holds a value and a reference to the next node in the list.
The list has a head, which points to the first node, and a tail, which points to the last node.
The nodes are not stored next to each other in memory; each node holds the address of the next node.
To access an element, you need to start from the head and move through the list to find the desired node, as there is no direct access to any specific node.
Application of Singly Linked Lists:
Memory management: Singly linked lists can be used to create memory pools, where memory is given out and taken back as needed.
Database indexing: Singly linked lists can be used in databases to allow quick insertion and deletion of data.
Representing polynomials and sparse matrices: Singly linked lists can efficiently represent polynomials and sparse matrices, where most elements are zero.
Operating systems: Singly linked lists are used in operating systems for tasks like scheduling processes and managing system resources.
Advantages of Singly Linked Lists:
Dynamic memory allocation: Singly linked lists let you change the size of the list at runtime by adding or removing elements.
Cache friendliness: Singly linked lists can be cache-friendly because nodes can be stored in separate cache lines, reducing cache misses and improving performance.
Space-efficient: Singly linked lists are space-efficient since each element only needs to store a reference to the next node, not a large block of contiguous memory.
Disadvantages of Singly Lists:
Poor random access performance: Accessing an element in a singly linked list requires going through the list from the head to the desired node, making it slower for random access compared to arrays.
Increased memory overhead: Singly linked lists need extra memory to store pointers to the next node in each element, leading to more memory usage compared to arrays.
Vulnerability to data loss: Singly linked lists can lose data if a node’s next pointer is lost or corrupted, as there is no way to access other elements in the list.
Not suitable for parallel processing: Singly linked lists are not good for parallel processing because updating a node needs exclusive access to its next pointer, which is hard to manage in parallel environments.
Backward traversing not possible: Singly linked lists do not support backward traversing.