Boundaries Change the Game

Reversing an entire linked list is clean. One pointer starts at the head, marches to the end, flips every arrow along the way. No edge cases about where to start or stop. No leftover segments that need reconnecting. The boundary is implicit: start at the first node, stop when curr hits null.

Sublist reversal shatters that simplicity. The problem says: given positions m and n, reverse only the nodes between those positions and leave the rest of the list intact. The node at position m is the first to flip. The node at position n is the last. Everything before m stays forward. Everything after n stays forward. Only the interior gets reversed.

This is LC 92Reverse Linked List II — and it looks deceptively similar to the basic reversal. Same four-step loop body inside: save, sever, advance prev, advance curr. But the bookkeeping around that loop changes completely. You need to answer three questions before the first pointer flips:

Where does the reversal start? A singly-linked list has no random access. You cannot jump to position m. You have to walk there, one .next at a time, counting as you go. That walk is not overhead — it is setup. The node you stop at before entering the reversal zone is the most important node in the entire algorithm.

123456null

The walker starts before the list. Tap Walk to advance.

What must be saved before entering the zone? Two things. First: a reference to the node at position m-1. This node's .next pointer currently points at the first node in the reversal zone. After the zone is reversed, that .next must be rewritten to point at the new head of the reversed sublist — which is the node that was at position n. If you do not save a reference to m-1, you have no way to perform this rewrite. The second save: a reference to the node at position m itself. This node will become the tail of the reversed sublist. Its .next must eventually point at the node at position n+1 — the first node after the reversal zone.

These two saved references — connection and tail — are the anchors that will stitch the reversed sublist back into the chain. Without them, the reversal loop produces a beautiful reversed segment floating in the void, connected to nothing.

123456connectiontail

If you flip the arrows in zone 2–4 right now, which edges do you think will break? Tap Begin reversal to find out.

When does the reversal stop? The basic reversal runs until curr === null. The sublist reversal must stop after exactly n - m iterations. Overshoot by one and you flip a node that should stay forward. Undershoot and the sublist is incomplete. The loop counter is a hard boundary, not a convenience — it is the only thing preventing the reversal from consuming the rest of the list.

Think of it as surgery, not demolition. The basic reversal tears down the entire chain and rebuilds it backward. Sublist reversal cuts a precise incision, reverses the exposed segment, and sutures the wound closed. The incision is the walk. The reversal is the same four-step loop. The suture is two pointer assignments: connection.next = reversedHead and tail.next = nodeAfterZone. Those two assignments are trivial to write — but only if you saved the right references before the loop started.

123456

Every arrow flips. The entire structure is torn down.

Below, you have a six-node list: [1, 2, 3, 4, 5, 6]. The task is to reverse positions 2 through 4. That means nodes 2, 3, and 4 flip, while nodes 1, 5, and 6 stay put. What will the chain look like afterward? Hold your prediction before moving on — you will verify it inside the interactive.

Before you write any code, you need to understand why the walk to position m is not just traversal — it is the setup that makes everything else possible.

Reverse the Middle

1 / 8

reverse positions 2 through 4

Why must we walk to position m before reversing?

The Anatomy of Bounded Reversal

The disconnect is not a bug. It is the intermediate state that every sublist reversal passes through. After the reversal loop runs on positions 2 through 4, you have three segments: the prefix [1], the reversed sublist [4, 3, 2], and the suffix [5, 6]. Two connections are broken — the one between the prefix and the reversed sublist, and the one between the reversed sublist and the suffix.

Tap each segment below to see why the disconnect exists and what each anchor point does:

This is exactly what you saw in the interactive. Node 1's .next still points at node 2 — but node 2 is now the tail of the reversed sublist, not the head. And node 4, which is now the head of the reversed sublist, is not pointed to by anything in the prefix. Meanwhile, node 2's .next was overwritten during the reversal to point at node 3 (backward), so the path from node 2 to node 5 is gone.

The reversal loop does not know about boundaries. It runs the same save-sever-advance rhythm from SC-1, blissfully unaware that it is operating on a sublist. All boundary awareness lives in the code around the loop: the walk that parks connection at position m-1, the save that captures tail at position m, the loop counter that stops after n - m iterations, and the two stitching assignments that reconnect the segments.

In code, the full pattern is:

1
function reverseBetween(head: ListNode | null, m: number, n: number) {
2
  const dummy = { next: head } as ListNode
3
  let curr = dummy
4
  for (let i = 0; i < m - 1; i++) curr = curr.next  // walk
5
  const connection = curr          // save m-1 (dummy when m=1)
6
  const tail = connection.next     // save m (will be tail)
7
  let prev: ListNode | null = null
8
  curr = tail
9
  for (let i = 0; i < n - m; i++) {
10
    const next = curr.next         // save bookmark
11
    curr.next = prev               // sever
12
    prev = curr                    // advance prev
13
    curr = next                    // advance curr
14
  }
15
  connection.next = prev           // stitch left
16
  tail.next = curr                 // stitch right
17
  return dummy.next
18
}

Notice the structure. Lines 1-3 are the walk. Lines 4-5 are the saves. Lines 6-12 are the reversal loop — identical to the full-list reversal from SC-1 and SC-2, just bounded by a counter instead of curr !== null. Lines 13-14 are the stitch. That is the entire algorithm: walk, save, reverse, stitch.

The common mistake is attempting the stitch before understanding what prev and curr point to after the loop. After n - m iterations: prev is the new head of the reversed sublist (node 4). curr is the first node after the reversal zone (node 5). So connection.next = prev bridges the prefix to the new head, and tail.next = curr bridges the old head (now tail) to the suffix. Four variables, two assignments, zero mystery — as long as you saved connection and tail before the loop.

There is one edge case worth noting: when m === 1, there is no prefix. The walk takes zero steps, connection would be invalid, and the reversed sublist's head becomes the new list head. Most implementations handle this with a dummy node prepended to the list — dummy.next = head, walk starts from dummy, and the return value is dummy.next. This sidesteps the edge case entirely.

The next screen picks up exactly where this one left off: three disconnected segments, two broken wires, and the task of stitching them back together.