Skip to content
Codeloom
DSA

Linked List Reversal Patterns

Master every linked list reversal pattern — iterative, recursive, reverse in groups of K, and reverse between positions m and n, with Python code and common pitfalls.

·10 min read · By Codeloom
Intermediate 17 min read

What you'll learn

  • Iterative reversal with the three-pointer technique
  • Recursive reversal and how the call stack unwinds
  • Reversing in groups of K nodes
  • Reversing between positions m and n (partial reversal)
  • Common mistakes and how to avoid them
  • When to pick iterative vs recursive

Prerequisites

Iterative linked list reversal step by step

Reversing a linked list is one of the most frequently asked interview questions. It appears as a standalone problem and as a building block inside harder problems (palindrome check, reverse in K-groups, reorder list). Let’s master every variant.

Pattern 1: Iterative Reversal

The iterative approach uses three pointers — prev, curr, and nxt — to reverse each link one at a time:

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


def reverse_iterative(head):
    """
    Reverse the entire linked list iteratively.
    Time: O(n), Space: O(1)
    """
    prev = None
    curr = head

    while curr:
        nxt = curr.next   # 1. Save the next node
        curr.next = prev  # 2. Reverse the pointer
        prev = curr       # 3. Move prev forward
        curr = nxt        # 4. Move curr forward

    return prev  # prev is the new head

Step-by-step trace

For the list 1 -> 2 -> 3 -> 4 -> None:

StepprevcurrnxtList State
InitNone1-1→2→3→4→None
1122None←1 2→3→4→None
2233None←1←2 3→4→None
3344None←1←2←3 4→None
44NoneNoneNone←1←2←3←4

At the end, prev points to node 4 — the new head.

Common mistake: forgetting to save nxt

If you write curr.next = prev before saving curr.next, you lose the reference to the rest of the list. Always save nxt = curr.next first.

Pattern 2: Recursive Reversal

The recursive approach reverses the rest of the list first, then fixes the current node:

def reverse_recursive(head):
    """
    Reverse the entire linked list recursively.
    Time: O(n), Space: O(n) — call stack
    """
    # Base case: empty or single node
    if not head or not head.next:
        return head

    # Recurse on the rest
    new_head = reverse_recursive(head.next)

    # head.next is the last node of the reversed sublist
    # Make it point back to head
    head.next.next = head
    head.next = None

    return new_head

How the recursion unwinds

For 1 -> 2 -> 3 -> None:

reverse(1)
  reverse(2)
    reverse(3) → returns 3 (base case)
    2.next.next = 2  → 3.next = 2
    2.next = None     → 3 -> 2 -> None
    return 3
  1.next.next = 1  → 2.next = 1
  1.next = None     → 3 -> 2 -> 1 -> None
  return 3

The new head (3) bubbles up through every return.

Iterative vs recursive — when to choose

FactorIterativeRecursive
SpaceO(1)O(n) call stack
SpeedSlightly fasterStack overhead
ReadabilityClear loopElegant but tricky
Stack overflow riskNoneYes, for huge lists
Interview preferencePreferredGood to know both

Use iterative for production code — it is O(1) space and will not stack-overflow on large lists.

Pattern 3: Reverse Between Positions m and n

Reverse only the sublist from position m to position n (1-indexed), leaving the rest intact.

Example: 1 -> 2 -> 3 -> 4 -> 5, m=2, n=4 becomes 1 -> 4 -> 3 -> 2 -> 5.

def reverse_between(head, m, n):
    """
    Reverse nodes from position m to n (1-indexed).
    Time: O(n), Space: O(1)
    """
    if not head or m == n:
        return head

    # Use a dummy node to handle edge case where m = 1
    dummy = ListNode(0)
    dummy.next = head
    pre = dummy

    # Move pre to the node just before position m
    for _ in range(m - 1):
        pre = pre.next

    # Start reversing from position m
    curr = pre.next
    for _ in range(n - m):
        # Pull the next node out and insert it after pre
        nxt = curr.next
        curr.next = nxt.next
        nxt.next = pre.next
        pre.next = nxt

    return dummy.next

How the “pull and insert” works

Instead of reversing pointers, we repeatedly pull the next node and insert it right after pre:

Original:  pre -> [2] -> 3 -> 4 -> 5   (m=2, n=4)

Iteration 1: Pull 3, insert after pre
           pre -> [3] -> [2] -> 4 -> 5

Iteration 2: Pull 4, insert after pre
           pre -> [4] -> [3] -> [2] -> 5

This avoids needing to reconnect the reversed segment to the list — the connections are maintained throughout.

Pattern 4: Reverse in Groups of K

Given a linked list, reverse every consecutive group of K nodes. If the last group has fewer than K nodes, leave it as-is (or reverse it, depending on the variant).

Example: 1 -> 2 -> 3 -> 4 -> 5, K=3 becomes 3 -> 2 -> 1 -> 4 -> 5.

def reverse_k_group(head, k):
    """
    Reverse nodes in groups of k.
    If remaining nodes < k, leave them unchanged.
    Time: O(n), Space: O(1)
    """
    # First check if there are at least k nodes remaining
    count = 0
    node = head
    while node and count < k:
        node = node.next
        count += 1

    if count < k:
        return head  # Not enough nodes — don't reverse

    # Reverse k nodes
    prev = None
    curr = head
    for _ in range(k):
        nxt = curr.next
        curr.next = prev
        prev = curr
        curr = nxt

    # head is now the tail of the reversed segment
    # Connect it to the result of reversing the remaining list
    head.next = reverse_k_group(curr, k)

    return prev  # prev is the new head of this segment


# Iterative version (avoids recursion stack)
def reverse_k_group_iterative(head, k):
    """
    Reverse nodes in groups of k — fully iterative.
    Time: O(n), Space: O(1)
    """
    dummy = ListNode(0)
    dummy.next = head
    group_prev = dummy

    while True:
        # Check if k nodes exist
        kth = group_prev
        for _ in range(k):
            kth = kth.next
            if not kth:
                return dummy.next  # Done — fewer than k remaining

        # Reverse k nodes starting from group_prev.next
        group_next = kth.next  # Save the node after this group
        prev = group_next
        curr = group_prev.next

        for _ in range(k):
            nxt = curr.next
            curr.next = prev
            prev = curr
            curr = nxt

        # Connect the previous part to the reversed group
        first_in_group = group_prev.next  # This is now the tail
        group_prev.next = prev  # prev is now the head
        group_prev = first_in_group  # Move to end of reversed group

Testing all patterns

def to_list(head):
    """Convert linked list to Python list for easy viewing."""
    result = []
    while head:
        result.append(head.val)
        head = head.next
    return result

def from_list(arr):
    """Create linked list from Python list."""
    dummy = ListNode(0)
    curr = dummy
    for val in arr:
        curr.next = ListNode(val)
        curr = curr.next
    return dummy.next


# Test iterative reversal
head = from_list([1, 2, 3, 4, 5])
print(to_list(reverse_iterative(head)))  # [5, 4, 3, 2, 1]

# Test recursive reversal
head = from_list([1, 2, 3, 4, 5])
print(to_list(reverse_recursive(head)))  # [5, 4, 3, 2, 1]

# Test reverse between m=2, n=4
head = from_list([1, 2, 3, 4, 5])
print(to_list(reverse_between(head, 2, 4)))  # [1, 4, 3, 2, 5]

# Test reverse in K-groups (K=3)
head = from_list([1, 2, 3, 4, 5])
print(to_list(reverse_k_group(head, 3)))  # [3, 2, 1, 4, 5]

# Test reverse in K-groups (K=2)
head = from_list([1, 2, 3, 4, 5])
print(to_list(reverse_k_group(head, 2)))  # [2, 1, 4, 3, 5]

Common Mistakes

1. Losing the rest of the list

# WRONG — nxt not saved before overwriting curr.next
def reverse_wrong(head):
    prev = None
    curr = head
    while curr:
        curr.next = prev  # Lost curr.next!
        prev = curr
        curr = curr.next  # This is now prev, not the original next
    return prev

2. Not using a dummy node for reverse_between

When m = 1, you are reversing from the head. Without a dummy node, you need special-case code. The dummy node eliminates this.

3. Off-by-one errors in K-group reversal

Make sure you count exactly K nodes before reversing. If you count K+1 or K-1, the groups will be wrong.

4. Forgetting to reconnect segments

After reversing a sublist, the old head of the sublist is now the tail. You must connect it to whatever comes after.

Reversal as a Building Block

Many harder problems use reversal internally:

def is_palindrome(head):
    """
    Check if a linked list is a palindrome.
    Uses: find middle + reverse second half + compare.
    Time: O(n), Space: O(1)
    """
    # Find middle
    slow = head
    fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next

    # Reverse second half
    second_half = reverse_iterative(slow)

    # Compare both halves
    first = head
    second = second_half
    result = True
    while second:
        if first.val != second.val:
            result = False
            break
        first = first.next
        second = second.next

    # Restore the list (optional but good practice)
    reverse_iterative(second_half)

    return result


def reorder_list(head):
    """
    Reorder: L0→L1→...→Ln  becomes  L0→Ln→L1→Ln-1→...
    Uses: find middle + reverse + merge.
    Time: O(n), Space: O(1)
    """
    if not head or not head.next:
        return

    # Find middle
    slow, fast = head, head
    while fast.next and fast.next.next:
        slow = slow.next
        fast = fast.next.next

    # Reverse second half
    second = reverse_iterative(slow.next)
    slow.next = None  # Cut the list

    # Merge alternating
    first = head
    while second:
        tmp1 = first.next
        tmp2 = second.next
        first.next = second
        second.next = tmp1
        first = tmp1
        second = tmp2

Complexity Summary

PatternTimeSpace
Iterative full reversalO(n)O(1)
Recursive full reversalO(n)O(n)
Reverse between m and nO(n)O(1)
Reverse K-group (recursive)O(n)O(n/k)
Reverse K-group (iterative)O(n)O(1)

Practice Problems

  1. LeetCode 206 — Reverse Linked List: Full reversal, both iterative and recursive (Easy)
  2. LeetCode 92 — Reverse Linked List II: Reverse between positions m and n (Medium)
  3. LeetCode 25 — Reverse Nodes in k-Group: The hardest reversal problem (Hard)
  4. LeetCode 234 — Palindrome Linked List: Reverse + compare (Easy)
  5. LeetCode 143 — Reorder List: Find middle + reverse + merge (Medium)
  6. LeetCode 24 — Swap Nodes in Pairs: K-group with K=2 (Medium)

Key Takeaways

  • The iterative three-pointer technique (prev, curr, nxt) is the foundation — memorize it.
  • The “pull and insert” method for partial reversal avoids reconnection headaches.
  • Always use a dummy node when the head might change.
  • Reversal is a building block for palindrome checks, list reordering, and K-group problems.
  • Prefer iterative over recursive in production — O(1) space, no stack overflow risk.