Singly vs Doubly Linked List: Key Differences and When to Use Each
singly linked list stores nodes with a reference to the next node only, allowing one-way traversal. A doubly linked list stores nodes with references to both the next and previous nodes, enabling two-way traversal but using more memory.Quick Comparison
This table summarizes the main differences between singly and doubly linked lists.
| Factor | Singly Linked List | Doubly Linked List |
|---|---|---|
| Node Structure | Contains data and next pointer | Contains data, next pointer, and previous pointer |
| Traversal | One direction (forward) | Two directions (forward and backward) |
| Memory Usage | Less memory per node | More memory per node due to extra pointer |
| Insertion/Deletion | Efficient at head, slower at tail | Efficient at both head and tail |
| Complexity | Simpler to implement | More complex due to extra pointer management |
| Use Cases | When memory is limited and simple traversal suffices | When bidirectional traversal or easy deletion is needed |
Key Differences
A singly linked list is made of nodes where each node points only to the next node. This means you can only move forward through the list. It uses less memory because each node stores just one pointer.
In contrast, a doubly linked list has nodes with two pointers: one to the next node and one to the previous node. This allows moving both forward and backward through the list, which makes some operations like deletion or reverse traversal easier and faster.
However, the extra pointer in doubly linked lists means they use more memory and require more careful pointer updates during insertions and deletions to avoid errors. Singly linked lists are simpler and use less memory but are limited in navigation and some operations.
Code Comparison
Here is a simple example of inserting a new node at the head of a singly linked list and printing the list.
class Node: def __init__(self, data): self.data = data self.next = None class SinglyLinkedList: def __init__(self): self.head = None def insert_at_head(self, data): new_node = Node(data) new_node.next = self.head self.head = new_node def print_list(self): current = self.head while current: print(current.data, end=' -> ') current = current.next print('None') # Usage sll = SinglyLinkedList() sll.insert_at_head(3) sll.insert_at_head(2) sll.insert_at_head(1) sll.print_list()
Doubly Linked List Equivalent
This example shows inserting a new node at the head of a doubly linked list and printing it forward and backward.
class Node: def __init__(self, data): self.data = data self.next = None self.prev = None class DoublyLinkedList: def __init__(self): self.head = None self.tail = None def insert_at_head(self, data): new_node = Node(data) new_node.next = self.head if self.head: self.head.prev = new_node else: self.tail = new_node self.head = new_node def print_forward(self): current = self.head while current: print(current.data, end=' -> ') current = current.next print('None') def print_backward(self): current = self.tail while current: print(current.data, end=' -> ') current = current.prev print('None') # Usage dll = DoublyLinkedList() dll.insert_at_head(3) dll.insert_at_head(2) dll.insert_at_head(1) dll.print_forward() dll.print_backward()
When to Use Which
Choose a singly linked list when memory is limited and you only need to traverse the list in one direction, such as implementing simple queues or stacks.
Choose a doubly linked list when you need to traverse both forwards and backwards, or when you want faster insertions and deletions from both ends or from the middle of the list, like in navigation systems or undo functionality.