Phase 2 · Linear StructuresModule 6~32 min read

Doubly & Circular Linked Lists

Add a backward pointer for two-way traversal, and join the ends to form circular lists.

What you'll learn

A singly linked list can only move forward. Add a second pointer — prev — and you get a doubly linked list you can traverse both ways and delete from in O(1). Join the ends and you get a circular list with no beginning or end.

By the end you'll be able to:

  • Explain what the prev pointer buys you
  • Delete a known node in O(1) without tracking the predecessor
  • Describe circular lists and where they shine

The prev pointer

Each node now stores two pointers: next and prev. You can walk backward as easily as forward, and every node knows both of its neighbors. The cost is one extra pointer per node and a little more bookkeeping on every insert and delete (you maintain two links instead of one).

O(1) deletion

This is the big win. In a singly list you had to walk from the head to find a node's predecessor before you could unlink it. In a doubly list the node already points to its predecessor, so removal is a pure pointer rewire:

Deleting a known node in a doubly linked list
delete(20)
HEAD
10
⇄
20
⇄
TAIL
30
⇄ null
1/3A doubly linked list: each node has both a next and a prev pointer, so you can walk in either direction.
Both neighbors are updated: prev.next and next.prev.

Circular lists

In a circular linked list the last node's next points back to the head (and in a circular doubly list, the head's prev points to the tail). There's no null terminator — you can loop around forever:

Traversing a circular list
loop around

↺ the last node's pointer links back to the head

HEAD
10
→
20
→
30
↺
1/4A circular list: the last node's next points back to the head. Start at 10.

In code

Language
doubly.py
class Node:
    def __init__(self, value):
        self.value = value
        self.prev = None
        self.next = None

def delete(node):          # O(1) — no search needed if you hold the node
    if node.prev:
        node.prev.next = node.next
    if node.next:
        node.next.prev = node.prev

Where they're used

StructureGreat for
Doubly linkedDeques, browser history (back/forward), text editors, LRU caches
CircularRound-robin scheduling, turn-based games, buffering, playlists on repeat
Circular doublyThe standard implementation of a deque; OS process scheduling

Key idea

The doubly linked list is the backbone of the deque (Module 8) and the LRU cache (Module 30) — both need fast insertion and removal at both ends.

Recap & quick check

Key takeaways

  • A doubly linked list adds a prev pointer, enabling two-way traversal.
  • Deleting a known node is O(1): rewire prev.next and next.prev — no predecessor search.
  • The cost is one extra pointer per node and maintaining two links on each edit.
  • A circular list joins the tail's next back to the head — no null terminator.
  • Doubly + circular lists power deques, LRU caches, and round-robin scheduling.

Quick check

1. What does the prev pointer enable?

2. Deleting a known node in a doubly linked list is:

3. In a circular linked list, the last node's next points to:

4. Which is a natural use of a circular list?

You've mastered chains of nodes. Now we constrain where you can add and remove — and get two of the most useful structures in computing. Next up: Module 7 — Stacks.