Reverse a Doubly Linked List: Iterative, Recursive, and Common Mistakes

Reverse a Doubly Linked List: Iterative, Recursive, and the Common Mistakes

Reversing a doubly linked list (DLL) is a classic interview question that tests pointer manipulation under more constraints than the singly-linked variant. Each node has both prev and next pointers, and a correct reversal swaps both pointers at every node while updating the head reference. The base problem is simple in concept but easy to get wrong under pressure — most candidates trip on either pointer order or the head update. This guide covers the iterative and recursive approaches with working code and the mistakes that show up in interviews.

Problem Statement

Given the head of a doubly linked list, reverse it in place and return the new head. Each node has val, prev, and next; the original head becomes the new tail.

Examples:

  • Input: 1 ⇄ 2 ⇄ 3 ⇄ 4 → Output: 4 ⇄ 3 ⇄ 2 ⇄ 1
  • Input: empty list → Output: empty list
  • Input: single node → unchanged

Approach 1: Iterative Pointer Swap (Standard Answer)

Walk the list, swapping prev and next at each node. The “next” node before the swap becomes the next iteration’s target. After processing the last node, return it as the new head.

class DLLNode:
    def __init__(self, val=0, prev=None, next=None):
        self.val = val
        self.prev = prev
        self.next = next


def reverse_dll(head: DLLNode) -> DLLNode:
    """O(n) time, O(1) space."""
    current = head
    new_head = head

    while current:
        # Swap prev and next pointers
        current.prev, current.next = current.next, current.prev
        new_head = current
        # Advance: original next is now in current.prev
        current = current.prev

    return new_head


# Test helper to print the list forward and backward
def to_forward(head):
    result = []
    while head:
        result.append(head.val)
        if not head.next:
            tail = head
        head = head.next
    return result, tail

def to_backward(tail):
    result = []
    while tail:
        result.append(tail.val)
        tail = tail.prev
    return result


# Build 1 ⇄ 2 ⇄ 3 ⇄ 4
nodes = [DLLNode(i) for i in range(1, 5)]
for i in range(3):
    nodes[i].next = nodes[i+1]
    nodes[i+1].prev = nodes[i]

new_head = reverse_dll(nodes[0])
fwd, tail = to_forward(new_head)
bwd = to_backward(tail)
print(fwd)  # [4, 3, 2, 1]
print(bwd)  # [1, 2, 3, 4]

Complexity: O(n) time, O(1) extra space.

Why the swap-then-advance pattern works: after we swap prev and next on the current node, the original next direction is preserved in current.prev (since we swapped). So we advance via current.prev, which is the original next.

Approach 2: Recursive Reversal

Recurse to the end, then unwind, swapping pointers as the call stack unwinds. O(n) stack space; useful when the interviewer asks for a recursive variant.

def reverse_dll_recursive(head: DLLNode) -> DLLNode:
    """O(n) time, O(n) stack space."""
    if not head:
        return None

    # Swap this node's pointers
    head.prev, head.next = head.next, head.prev

    # If head.prev (original next) is None, we're at the new head
    if not head.prev:
        return head

    # Recurse on the original next (now in head.prev)
    return reverse_dll_recursive(head.prev)

Same time complexity; uses recursion stack space proportional to list length. For very long lists, prefer the iterative version.

Approach 3: Two-Pointer Walk (Alternative)

Use a temporary variable to remember the original next, then swap and advance. Conceptually equivalent to Approach 1 but spelled out more explicitly.

def reverse_dll_explicit(head: DLLNode) -> DLLNode:
    """O(n) time, O(1) space — explicit temp variable."""
    current = head
    new_head = None

    while current:
        # Save original next
        next_node = current.next
        # Swap prev and next
        current.next = current.prev
        current.prev = next_node
        # Track the new head (the last node we processed)
        new_head = current
        # Advance to the original next
        current = next_node

    return new_head

Some find this version easier to reason about because the temp variable makes the original next explicit. Pick whichever style feels cleaner; both are O(n) time and O(1) space.

Common Mistakes

  • Forgetting to update both pointers. Reversing only next while leaving prev alone produces a broken list — backwards traversal fails. Always swap both at every node.
  • Losing track of the new head. The new head is the last node visited (the original tail). Track it explicitly with a variable; don’t try to compute it after the loop.
  • Advancing in the wrong direction. After swapping, the original next direction is in current.prev. Advancing via current.next goes backward — toward already-processed nodes — and the loop terminates incorrectly.
  • Returning the original head. The original head becomes the new tail. Return the new head (last node visited), not head.
  • Not handling empty list and single-node cases. The standard implementation handles these via the loop condition (empty: loop body doesn’t execute; single node: one swap, no advance), but bugs in the head-tracking can manifest in these edge cases. Test them.
  • Memory issues with mutual references. In garbage-collected languages, mutual prev/next references aren’t a concern. In manual-memory languages (C, C++ without smart pointers), the reversal mutates pointers in place — no allocation needed, no leaks possible from the algorithm itself.

Common Variations

Reverse a singly linked list

(LeetCode #206) The simpler variant. Only one pointer per node; same iterative pattern with one swap per node.

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next


def reverse_singly(head: ListNode) -> ListNode:
    prev = None
    current = head
    while current:
        next_node = current.next
        current.next = prev
        prev = current
        current = next_node
    return prev

Reverse in groups of k

(LeetCode #25 — Reverse Nodes in k-Group) Reverse the first k nodes, then the next k, and so on. Apply the standard reversal to each group, stitching them together.

Reverse between positions m and n

(LeetCode #92) Reverse a sub-segment of the list. Walk to position m, reverse n-m nodes, reconnect to the surrounding nodes.

Check if a linked list is a palindrome

(LeetCode #234) Uses the singly-linked reversal as a subroutine: reverse the second half, compare to first half. See our palindrome guide.

Detect and remove cycles

If the DLL has a cycle (some node’s next points back to an earlier node), the reversal algorithm above doesn’t terminate. Detect cycles first via Floyd’s algorithm, or assume well-formed input.

Frequently Asked Questions

What’s the expected interview answer?

Iterative pointer swap, O(n) time and O(1) space. Walk through the algorithm: at each node, swap prev and next; advance via the new prev (which was the original next). Return the last visited node as the new head. Mention the recursive variant as an alternative; some interviewers explicitly want the iterative version.

Why is reversing a DLL different from reversing a singly linked list?

Both swap pointer directions, but the DLL has two pointers per node. The algorithm is structurally similar; the work per node doubles. The DLL version is conceptually slightly easier because the “where do I go next” answer is right there in current.prev after the swap — no separate tracking needed. The singly-linked version requires explicitly saving next before overwriting current.next.

How do I keep track of the new head?

Maintain a variable that holds the last visited node. At the end of the loop, that variable is the new head. Alternative: notice that when current.prev (the new “next direction”) becomes None, you’re at the new head — return it directly. Both work; the explicit variable is clearer for most candidates.

What if the doubly linked list is circular?

The standard algorithm doesn’t terminate on circular DLLs because the loop never reaches None. For circular DLLs, you’d track the original head and stop when you return to it. This is a different problem; clarify with the interviewer whether the input is circular before assuming.

How does this generalize to other data structures?

The “swap pointers and advance” pattern applies to any structure with bidirectional links: doubly linked lists, sequences in graphs, certain tree representations. The key insight — using one pointer to remember “where to go next” while you’re rewriting another — is reusable. Mastering DLL reversal often unlocks other linked-structure problems.

See also: Check if a Linked List is a PalindromeDetect a Cycle in a Linked ListSerialize and Deserialize a Binary Tree

💡Strategies for Solving This Problem

The Swap Pattern

Got this at Meta. It's simpler than reversing a singly linked list in some ways - you have both prev and next pointers to work with.

The Problem

Reverse the direction of all pointers in a doubly linked list. What was next becomes prev, what was prev becomes next.

Example:

Original: 1 <-> 2 <-> 3 <-> 4
Reversed: 4 <-> 3 <-> 2 <-> 1

Key Insight

For each node, swap its prev and next pointers. That's it. No need to track previous node like in singly linked list.

The Algorithm

  1. Start at head
  2. For each node: swap node.prev and node.next
  3. Move to next node (which is now in node.prev!)
  4. Return what was the last node (now the new head)

O(n) time, O(1) space. Single pass through the list.

The Tricky Part

After swapping pointers, "next" is now in the prev field. You need to move to node.prev to go forward. This confuses people.

Alternative: Three Pointer Approach

Keep track of prev, current, and next. Similar to singly linked list reversal but swap both directions. More verbose but maybe clearer.

At Meta

My interviewer asked "How is this different from reversing a singly linked list?" The key difference: you don't need to track the previous node separately because each node already has a prev pointer.

Then asked about edge cases: empty list, single node, two nodes. All work with the same code.

Scroll to Top