Doubly Linked List
Table of Contents + β
In a normal singly linked list each node only knows the node after it. So you can move in one direction only. You can go forward. That one-way street causes real pain. Say you are standing on a node and you want to delete it. To unlink it you need the node before it. But that node is behind you and you have no way to look back. So you end up walking the whole list from the start again just to find it.
A doubly linked list fixes this. A doubly linked list is a linked list where every node holds two pointers. One points to the next node. One points to the previous node. Now each node knows both neighbors. So you can walk forward. You can walk backward. And you can delete a node on the spot without searching.
π What Is a Doubly Linked List?
Think of people standing in a line holding hands. In a singly linked list each person only holds the hand of the person ahead. So you can pass a message forward but never backward. In a doubly linked list each person holds both hands. They hold the one ahead and the one behind. Now a message can travel either way. And if someone leaves the line the two neighbors just join hands directly.
Here is what makes a node βdoublyβ:
- data β the value the node stores.
- next β a pointer to the node that comes after it.
- prev β a pointer to the node that comes before it.
The first nodeβs prev points to nothing (null). The last nodeβs next points to nothing (null). Those two nulls are how you know you have reached the ends.
The solid arrows are the next pointers going forward. The dotted arrows are the prev pointers going backward.
π― Why Use One?
You reach for a doubly linked list when you need to move both ways, or when you want to delete fast.
- Walk backward. You can start at the last node and step back to the first using
prev. A singly list cannot do this. - Delete without searching. If you already hold the node you want to remove, you have its
prevandnextright there. So you unlink it in one step. No walking from the start. - Insert before a node easily. Adding a node before a known node is simple. You can reach the previous one directly.
Note
Real tools use this all the time. The back and forward buttons in your browser. The undo and redo in a text editor. A music player moving to the previous or next song. All of these fit a doubly linked list nicely because you move both ways.
π οΈ Steps to Build and Traverse a Doubly Linked List
Before the code, letβs lay out the plan in plain steps.
- Define the node. Give it three parts:
data, anextpointer, and aprevpointer. - Start empty. Keep a
headpointer for the first node. At the start it points to nothing. - Append a node at the end. Walk to the last node, set its
nextto the new node, and set the new nodeβsprevback to that last node. - Traverse forward. Start at
head, print the data, follownext, and stop when you hit null. Remember the last node as you go. - Traverse backward. Start at that last node, print the data, follow
prev, and stop when you hit null.
Now letβs turn those steps into real code.
π» Code: Build, Walk Forward, Walk Backward
We build a list with the values 10, 20, 30. Then we print it forward. Then we print it backward. Watch how the backward walk reuses the prev pointers we set while building.
# A node holds data, a next pointer, and a prev pointerclass Node: def __init__(self, value): self.data = value self.next = None self.prev = None
# Add a node at the end and link prev + next both waysdef append(head, value): node = Node(value) if head is None: # empty list, new node is head return node temp = head while temp.next is not None: # walk to the last node temp = temp.next temp.next = node # last node points forward to new node node.prev = temp # new node points back to last node return head
def main(): head = None head = append(head, 10) head = append(head, 20) head = append(head, 30)
# Walk forward from head, remember the last node print("Forward: ", end=" ") temp = head last = head while temp is not None: print(temp.data, end=" ") last = temp temp = temp.next print()
# Walk backward from the last node using prev print("Backward:", end=" ") temp = last while temp is not None: print(temp.data, end=" ") temp = temp.prev print()
main()The output of the above code will be:
Forward: 10 20 30Backward: 30 20 10Tip
See the trick in the backward walk? We never searched for the last node twice. While walking forward we kept saving the current node in last. So when the forward loop ends, last already holds the tail. It is ready for the backward walk.
β±οΈ Cost and Complexity
The good news is that the two-way movement is free in terms of speed. Following a prev pointer is just as fast as following a next pointer. Here is how the common operations stack up.
| Operation | Time | Why |
|---|---|---|
| Traverse forward or backward | O(n) | You visit every node once. |
| Append at the end | O(n) | You walk to the tail first (O(1) if you keep a tail pointer). |
| Delete a node you already hold | O(1) | You have its prev and next, so unlink in one step. |
| Search for a value | O(n) | You may need to check every node. |
So what is the catch? Every node now stores an extra prev pointer. That is more memory used per node compared to a singly linked list. You are trading a little space for the freedom to move both ways and to delete fast.
Caution
The most common bug is forgetting to set prev. If you link next but leave prev empty, your forward walk works fine and you think all is well. Then the backward walk breaks because the chain back is missing. Always set both pointers when you add a node.
π§© What Youβve Learned
- β
A doubly linked list gives every node two pointers:
nextto the node ahead andprevto the node behind. - β Those two pointers let you walk the list forward and backward.
- β
You can delete a node in O(1) when you already hold it, because its
prevandnextare right there. - β
Appending walks to the tail and links both directions: the old tailβs
nextand the new nodeβsprev. - β
The cost is extra memory for the
prevpointer in every node. - β
Forgetting to set
previs the classic bug, so always wire both pointers.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What two pointers does a node in a doubly linked list hold?
Why: Each node stores next (the node ahead) and prev (the node behind), which is what makes it doubly linked.
- 2
Why can you delete a node in O(1) when you already hold it?
Why: Holding the node means you already know both neighbors, so no walk from the start is needed.
- 3
What is the main cost of a doubly linked list compared to a singly linked list?
Why: Every node stores an extra prev pointer, so it uses more memory per node.
- 4
When you append a new node at the end, which links must you set?
Why: You point the old tail forward with next and point the new node back with prev, wiring both directions.