Doubly Linked Lists: Bidirectional Navigation Made Easy

Understanding Data Structures Through Big O Notation
In our previous discussion on Big O notation, we explored how it helps us measure the efficiency of algorithms. Now, let’s take the next logical step: understanding data structures in the context of Big O.
Why Does Big O Matter for Data Structures?
Every data structure is built for specific tasks. Some are great at fast lookups, while others make insertions and deletions easier. Understanding Big O notation helps us choose the right tool for the job.
What is a Doubly Linked List?
A doubly linked list is similar to a singly linked list, but instead of just one pointer to the next node, each node has two pointers:
One pointing to the next node (like a singly linked list).
One pointing to the previous node, allowing traversal in both directions.
This means you can move forward and backward through the list, making certain operations more efficient compared to a singly linked list.
Real-World Analogy: A Two-Way Train System
Imagine a train track with stations where each station has two exits:
One exit leads to the next station.
The other exit leads to the previous station.
Unlike a one-way track (singly linked list), you can travel both ways, making it easier to move back if you missed a stop.
Why Use Doubly Linked Lists?
✅ Good for:
Fast Insertions/Deletions (Bidirectional Access) → You don’t have to traverse the entire list to remove a node. (Big O: when node is known)
Efficient Forward and Backward Traversal → You can move in both directions without needing extra computation. (Big O: for traversal)
❌ Not good for:
More Memory Usage → Each node has two pointers instead of one, increasing storage requirements.
Slightly More Complex Operations → Managing two pointers per node requires careful handling.
Time Complexity Overview
| Operation | Big O Notation | Explanation |
|---|---|---|
| Access (indexing) | O(n) | Like traveling down the track to find a station. |
| Search | O(n) | You must stop at each station one by one. |
| Insert (at head or tail ) | O(1) | Adding a station at the beginning or end of the track |
| Insert (middle) | O(n) | You have to stop at each station until you reach the right one |
| Delete (at known location) | O(1) | Removing a station when you already know its location. |
| Delete (unknown node) | O(n) | You must search for the station first. |
Example: Inserting a Node at the Head
Since a doubly linked list tracks both previous and next nodes, inserting at the head requires updating two pointers instead of one.
Doubly Linked List Insertion Code Example (TypeScript)
class DoublyNode {
value: number;
next: DoublyNode | null;
prev: DoublyNode | null;
constructor(value: number) {
this.value = value;
this.next = null;
this.prev = null;
}
}
class DoublyLinkedList {
head: DoublyNode | null = null;
tail: DoublyNode | null = null;
insertAtHead(value: number): void {
const newNode = new DoublyNode(value);
if (!this.head) {
this.head = this.tail = newNode;
} else {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
}
}
printList(): void {
let current = this.head;
while (current) {
console.log(current.value);
current = current.next;
}
}
}
// Usage Example:
const myDoublyList = new DoublyLinkedList();
myDoublyList.insertAtHead(10);
myDoublyList.insertAtHead(20);
myDoublyList.insertAtHead(30);
myDoublyList.printList(); // Output: 30, 20, 10
How It Works:
Create a new node.
Set the new node’s next pointer to the old head.
Update the old head’s previous pointer to the new node.
Make the new node the head of the list.
🔹 Big O: because we don’t shift existing elements.
Common Mistakes (Pitfalls)
🚨 Mistake #1: Forgetting to Update Both Pointers
- If you don’t set
prevandnextcorrectly, the list might break.
🚨 Mistake #2: Increased Memory Usage
- Since each node stores two pointers, the memory footprint is higher than singly linked lists.
🚨 Mistake #3: Complexity in Middle Insertions/Deletions
- Removing or inserting a node in the middle requires managing both
prevandnextpointers carefully.
Best Practices
✔ Use Doubly Linked Lists When Frequent Bidirectional Traversal is Needed ✔ Always Update Both Pointers During Insertions/Deletions ✔ Consider Alternatives if Memory is a Concern
Conclusion
🎯 Key Takeaways:
Doubly linked lists allow forward and backward traversal.
Insertions and deletions are fast when the node location is known.
They use more memory than singly linked lists.
Big O helps us determine when doubly linked lists are a better choice than arrays or singly linked lists.
By mastering doubly linked lists, you’ll be well-prepared for more advanced data structures like trees and graphs! 🚀