Menu
Coddy logo textTech

Doubly Linked List

Last updated

A doubly linked list is a linked list where each node holds two pointers: next (to the following node) and prev (to the preceding one). That extra backward link lets you traverse in both directions and delete a node in O(1) when you already have a reference to it - because you can reach its predecessor directly instead of walking from the head. Press play above to watch nodes link and relink both ways.

The cost is one extra pointer per node and more bookkeeping: every insert and delete must update both next and prev on the neighbors. It's the structure behind many deques and LRU caches, where fast removal from both ends matters.

Time complexity

OperationComplexityNotes
Insert at head/tailO(1)With head and tail pointers
Delete a known nodeO(1)Reach prev directly - no walk
SearchO(n)Still linear scan
Access by indexO(n)No random access

Singly vs doubly linked list

AspectSinglyDoubly
Pointers per node1 (next)2 (next + prev)
Traverse backwardNoYes
Delete a known nodeO(n) (find prev)O(1)
Memory overheadLowerHigher

Worked example

Building [10, 20, 30] by appending, then deleting node 20:

StepStructureAction
StartnullEmpty list: head and tail are both null
Append 1010First node; head and tail both point to it, prev = next = null
Append 2010 <-> 20Set 10.next = 20 and 20.prev = 10; tail = 20
Append 3010 <-> 20 <-> 30Set 20.next = 30 and 30.prev = 20; tail = 30
Delete 2010 <-> 30Using 20.prev and 20.next: set 10.next = 30 and 30.prev = 10 in O(1)

When to use a doubly linked list

Use it whenAvoid it when
You need to traverse or splice in both directionsYou only ever move forward - a singly linked list is lighter
You delete arbitrary nodes you already hold a reference to (O(1))You mostly index by position - use an array for O(1) access
You implement a deque, LRU cache, or on-screen undo historyMemory is tight - the extra prev pointer doubles link overhead
Insertions and removals happen at both ends frequentlyThe data is small and rarely changes - array copies are cheaper

Doubly Linked List code

A clean, runnable Doubly Linked List implementation in Python, JavaScript, Java, C++, C. Pick a language, copy the code, or open it pre-loaded in the Coddy Playground.

Doubly Linked List code in Python

Python
1class Node:2    def __init__(self, value):3        self.value = value4        self.prev = None5        self.next = None6
7
8class DoublyLinkedList:9    def __init__(self):10        self.head = None11        self.tail = None12
13    def append(self, value):14        node = Node(value)15        if self.tail is None:16            self.head = self.tail = node17            return18        # Wire both directions: old tail <-> new node19        node.prev = self.tail20        self.tail.next = node21        self.tail = node22
23    def prepend(self, value):24        node = Node(value)25        if self.head is None:26            self.head = self.tail = node27            return28        node.next = self.head29        self.head.prev = node30        self.head = node31
32    def forward(self):33        values, current = [], self.head34        while current:35            values.append(current.value)36            current = current.next37        return values38
39    def backward(self):40        values, current = [], self.tail41        while current:42            values.append(current.value)43            current = current.prev44        return values45
46
47lst = DoublyLinkedList()48for value in [2, 3, 4]:49    lst.append(value)50lst.prepend(1)51
52print("Forward: ", lst.forward())53print("Backward:", lst.backward())
Run this code in the Python Playground

Doubly Linked List FAQ

What is the difference between a singly and doubly linked list?
A singly linked list has one pointer per node (next), so you can only move forward. A doubly linked list adds a prev pointer, letting you traverse backward and delete a node you hold a reference to in O(1) - at the cost of an extra pointer per node and updating two links on every change.
When is a doubly linked list worth the extra memory?
When you need backward traversal or O(1) deletion of arbitrary nodes you already reference - classic cases are deques (double-ended queues) and LRU caches, where entries are moved and evicted from both ends frequently.
Why can a doubly linked list delete a node in O(1)?
To remove a node you must connect its predecessor to its successor. In a singly linked list you'd have to walk from the head to find the predecessor (O(n)); in a doubly linked list the node's prev pointer gives you the predecessor directly, so the relinking is O(1).
Doubly linked list vs array: which should I use?
Use an array when you need O(1) access by index and cache-friendly iteration, and when insertions mostly happen at the end. Use a doubly linked list when you frequently insert or remove in the middle or at both ends given a node reference, since that is O(1) versus an array's O(n) shift. Arrays win on memory and locality; the list wins on structural edits.
What is a sentinel node in a doubly linked list?
A sentinel (or dummy) node is a placeholder that sits before the head and/or after the tail so the list is never truly empty. It removes the null checks for head, tail, and boundary inserts/deletes, letting every operation follow the same relinking code path. Many production LRU-cache implementations use two sentinels to avoid special-casing the ends.
What is the most common bug when deleting from a doubly linked list?
Forgetting to update head or tail when you delete the first or last node, which leaves a dangling pointer to a freed node. Always check whether node.prev is null (update head) or node.next is null (update tail) before rewiring the neighbors.
Coddy programming languages illustration

Master algorithms with Coddy

GET STARTED