Doubly Linked List

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.

prev

prev

prev

null

10

20

30

null

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 prev and next right 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.

  1. Define the node. Give it three parts: data, a next pointer, and a prev pointer.
  2. Start empty. Keep a head pointer for the first node. At the start it points to nothing.
  3. Append a node at the end. Walk to the last node, set its next to the new node, and set the new node’s prev back to that last node.
  4. Traverse forward. Start at head, print the data, follow next, and stop when you hit null. Remember the last node as you go.
  5. 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.

doubly_linked_list.py
# A node holds data, a next pointer, and a prev pointer
class 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 ways
def 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 30
Backward: 30 20 10

Tip

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: next to the node ahead and prev to 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 prev and next are right there.
  • βœ… Appending walks to the tail and links both directions: the old tail’s next and the new node’s prev.
  • βœ… The cost is extra memory for the prev pointer in every node.
  • βœ… Forgetting to set prev is 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. 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. 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. 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. 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.

πŸš€ What’s Next?

Share & Connect