Learn/DSA Patterns
DSA PatternsLinked Listseasy9 min read

Reverse a Linked List

Reversing a linked list means redirecting every 'next' pointer to point backward. Three pointers — prev, curr, next — perform this in a single O(n) pass with O(1) space. The partial-reverse variant (reverse only positions m to n) extends this skeleton with a precise four-step wiring pattern.

#linked-list#reverse#three-pointers#in-place#beginner#interview
Table of contents

Before we start

Reversing a linked list is the single most tested linked list operation in interviews. It also appears as a sub-step inside palindrome checking, reorder list, and k-group reversal. Get this so automatic that you can write it without thinking — then the harder problems become tractable. By the end you will be able to:

  • Write the full reversal from memory, correctly, in under 60 seconds.
  • Trace the three-pointer dance node by node on any example.
  • Handle the partial reversal (reverse positions m to n) by extending the same skeleton.

Picture this first (no code yet)

A real-life story

Imagine a chain of dominoes, each one leaning against the next in a single direction. You want to make them all lean the other way — without knocking them over and starting a chaotic cascade.

You work on one domino at a time, from left to right. Before you tip the current domino to lean backward, you use one hand to hold the next domino in place (so you don't lose the rest of the chain), and the other hand to note which domino is now the "new front" (the one you just reversed). Then you move to the next domino and repeat.

Three hands (three pointers): one holding the previous position (prev), one at the current domino (curr), and one peeking at the next (nxt). That is the reversal algorithm.


First, the slow way (so you feel the pain)

You could copy all node values into an array, reverse the array, then copy values back. O(n) time — but O(n) space for the array. For a linked list of 10 million nodes, that is 40–80 MB of extra memory just to flip directions. The three-pointer approach uses O(1) extra space — just three pointer variables, no matter how long the list.


The turning point

Pause & think

You are at node B in the list A → B → C → D. You want to make B point back to A. Before you do B.next = A, what one thing must you save? If you don't save it, what is lost forever?

Answer

You must save B.next (which is C) before overwriting it. Once you do B.next = A, the pointer to C is gone — you've lost the rest of the list. That saved reference is the nxt pointer in the algorithm. Always save nxt = curr.next as the very first line inside the loop, before changing any pointers.


The one idea to remember

The entire pattern in one sentence

Save the next node, redirect current's pointer backward to prev, advance prev and curr — three pointer assignments per node, one pass: O(n) time, O(1) space.


The code, line by line

def reverseList(head):
    prev = None      # the node that current should now point to
    curr = head      # the node we are currently processing

    while curr:
        nxt       = curr.next   # ① save next (don't lose the rest of the list)
        curr.next = prev        # ② reverse the pointer
        prev      = curr        # ③ advance prev
        curr      = nxt         # ④ advance curr

    return prev      # curr is None; prev is the new head

Four lines inside the loop. Commit them as a sequence: save, reverse, advance-prev, advance-curr.


Watch it happen, frame by frame

List: 1 → 2 → 3 → 4 → 5

Initial: prev=None, curr=1

Iteration 1:
  nxt       = 2
  curr.next = None   →   1 → None
  prev      = 1
  curr      = 2

  State: None ← 1    2 → 3 → 4 → 5

Iteration 2:
  nxt       = 3
  curr.next = 121 → None
  prev      = 2
  curr      = 3

  State: None ← 1 ← 2    3 → 4 → 5

Iteration 3:
  nxt       = 4
  curr.next = 2321 → None
  prev      = 3
  curr      = 4

Iteration 4:
  nxt       = 5
  curr.next = 3
  prev      = 4
  curr      = 5

Iteration 5:
  nxt       = None
  curr.next = 4
  prev      = 5
  curr      = None

While exits (curr = None).
Return prev = 5.

Final: 5 → 4 → 3 → 2 → 1 → None  ✅

Recursive version (for completeness)

def reverseList_recursive(head):
    if not head or not head.next:
        return head                       # base case: 0 or 1 node

    new_head = reverseList_recursive(head.next)   # reverse the rest
    head.next.next = head                # the node after head should point back to head
    head.next      = None                # head no longer points forward
    return new_head                      # new head is unchanged throughout recursion

The recursive version uses O(n) stack space — avoid it for very long lists. Iterative is O(1) space and preferred in interviews.


Variant: Reverse Linked List II — positions m to n (LC #92)

Reverse only the nodes from position m to position n (1-indexed).

1 → 2 → 3 → 4 → 5,  m=2, n=414325

The approach: walk to position m−1 (call it tail_before), then perform a standard reversal for n − m + 1 nodes, then reconnect.

def reverseBetween(head, m: int, n: int):
    dummy      = ListNode(0)     # dummy makes edge cases (m=1) uniform
    dummy.next = head
    prev       = dummy

    # Step 1: walk to the node just before position m
    for _ in range(m - 1):
        prev = prev.next
    # prev is now at position m-1

    # Step 2: reverse n-m+1 nodes starting at position m
    curr = prev.next   # this is the node at position m
    for _ in range(n - m):
        nxt        = curr.next
        curr.next  = nxt.next   # remove nxt from its current position
        nxt.next   = prev.next  # wire nxt to the front of the reversed section
        prev.next  = nxt        # update prev to point to nxt (new front)

    return dummy.next

Frame-by-frame: 1 → 2 → 3 → 4 → 5, m=2, n=4

After walk: prev=node1, curr=node2

Iteration 1 (move node3 to front of sublist):
  nxt       = node3
  curr.next = node4   →   sublist so far: node2 → node4 → node5
  nxt.next  = node2   →   node3 → node2 → node4 → node5
  prev.next = node3   →   node1 → node3 → node2 → node4 → node5

Iteration 2 (move node4 to front of sublist):
  nxt       = node4
  curr.next = node5   →   sublist: node2 → node5
  nxt.next  = node3   →   node4 → node3 → node2 → node5
  prev.next = node4   →   node1 → node4 → node3 → node2 → node5

Done. Answer: 1 → 4 → 3 → 2 → 5  ✅

Pause & think

In the partial-reversal loop, curr never moves — only prev.next and nxt change. Why does curr stay fixed? What role does it play across all iterations?

Answer

curr starts at the node at position m. After each iteration, a new node is inserted before curr in the sublist — curr ends up at the tail of the growing reversed section. Since we are always inserting nodes at the front (between prev and curr), curr naturally drifts to the end of the reversed region. After all iterations, curr.next is the first node after the reversed section. curr is the "anchor" — the node that stays at the tail of the reversed portion, and its curr.next always points to whatever hasn't been reversed yet.


Where to spot this pattern

Trigger words:

  • "reverse a linked list" (full or partial)
  • "reverse nodes in k-group" (Variant: reverse every k nodes — LC #25)
  • "palindrome linked list" — step 2 is a reversal
  • "reorder list" — step 2 is a reversal of the second half

Common traps

Watch out for these

  • Forgetting to save nxt before overwriting curr.next. This is the most common bug. Execution order must be: nxt = curr.next FIRST, then curr.next = prev.
  • Returning curr instead of prev at the end. After the loop, curr is None (it walked off the end). prev is the new head. Always return prev.
  • Not using a dummy node for partial reversal with m=1. If m=1, there is no "node before position m" — the head changes. A dummy node at position 0 unifies this edge case: prev starts at the dummy.
  • Confusing the full-reverse three-pointer (save nxt, flip curr.next, advance both) with the partial-reverse pattern (remove nxt from chain, wire to front, advance neither curr nor prev). They look similar but the partial-reverse loop body is different. Practise both separately.

Remember this forever

Full Reverse — 4 lines inside while:

prev, curr = None, head
while curr:
    nxt       = curr.next   # ① save
    curr.next = prev        # ② flip
    prev      = curr        # ③ advance prev
    curr      = nxt         # ④ advance curr
return prev                 # new head

Partial Reverse (m to n) — insert at front:

# walk to position m-1 (prev), curr = node at m
for _ in range(n - m):
    nxt       = curr.next
    curr.next = nxt.next    # skip nxt in chain
    nxt.next  = prev.next   # wire nxt to front
    prev.next = nxt         # update entry point

Trap: save nxt FIRST. Return prev, not curr.


Check yourself

After reversing `1 → 2 → 3 → 4 → 5`, what does the original `head` node (node 1) point to?

After the reversal, node 1's next pointer was set to None (it was prev = None when curr was on node 1). Node 1 is now the tail of the reversed list. So head.next = None, and head points to nothing — it is the last node. The variable head still holds a reference to node 1, but the new head of the list is prev (node 5). If you returned head instead of prev, you'd be returning the tail, not the head.

In the partial reversal, why do we use a dummy node?

The standard walk "go to position m−1" assumes a node at position m−1 exists. If m=1, position 0 doesn't exist in the original list. A dummy node at position 0 (pointing to head) gives prev a valid starting point even when m=1. After the reversal, dummy.next is the new head (which may have changed if m=1), and we return dummy.next rather than the possibly-stale head.


Practice problems

ProblemDifficultyWhat to noticeLink
Reverse Linked ListEasyFour-line loop; return prev not currLC #206
Reverse Linked List IIMediumDummy + walk + insert-at-front loopLC #92
Reverse Nodes in k-GroupHardReverse every k nodes; chain the reversed groupsLC #25
Palindrome Linked ListEasyMiddle → full reverse of second half → compareLC #234

Next up: Merge Two Sorted Lists — the compare-heads, pick-smaller, advance-pointer dance that is the merge step in merge sort and the foundation for Merge K Sorted Lists.