Problem
Given the head of a singly linked list and two 1-indexed positions left and right, reverse only the nodes between those two positions and return the modified list. Everything before position left and after position right remains in its original order.
[1,2,3,4,5]
- Input: head = [1,2,3,4,5], left = 2, right = 4
- Output: [1,4,3,2,5]
- Explanation: The sublist from position 2 to 4 is reversed in place; nodes 1 and 5 are untouched.
Intuition
The challenge is reversing a bounded section without disturbing the rest of the list. The key insight is to locate the node just before position left โ call it the anchor โ then repeatedly peel off the node immediately after curr and drop it right after the anchor. Each relocation prepends one more element to the growing reversed section, so the entire reversal completes in a single pass with no extra memory.
Approach 1 โ Collect, Reverse, Overwrite
Gather every node value into an array, reverse the slice from index left-1 to right-1, then walk the list a second time writing the values back. The structure of the list never changes โ only the values inside nodes are swapped.
- Traverse the list once, appending each node's value to an array.
- Use two indices
left_idx = left - 1andright_idx = right - 1, swapping inward until they meet. - Walk the list again, overwriting each node's value from the array in order.
- Return
headโ the same node that came in, now with rearranged values.
1def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:
2 values = []
3 curr = head
4 while curr:
5 values.append(curr.val)
6 curr = curr.next
7
8 left_idx, right_idx = left - 1, right - 1
9 while left_idx < right_idx:
10 values[left_idx], values[right_idx] = values[right_idx], values[left_idx]
11 left_idx += 1
12 right_idx -= 1
13
14 curr = head
15 for val in values:
16 curr.val = val
17 curr = curr.next
18
19 return headTime: O(n) โ two full traversals plus an O(right โ left) swap pass, all linear.
Space: O(n) โ the auxiliary array stores every node's value.
Approach 2 โ In-Place Insertion (Optimal)
Attach a dummy node before head so that prev always has a predecessor regardless of whether left = 1. Advance prev to the node just before the reversal region, then repeat right โ left times: peel off the node immediately after curr and splice it in right after prev. Each step prepends one more element to the reversed section. curr never moves โ it is the permanent tail of the reversed portion.
- Create a
dummynode withdummy.next = headto avoid special-casingleft = 1. - Advance
prevexactlyleft - 1steps so it sits at the node just before the reversal region. - Set
curr = prev.nextโ this node becomes the fixed tail of the reversed section. - Repeat
right - lefttimes: savenext_node = curr.next, unlink it, then splice it directly afterprev. - Return
dummy.next.
1def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:
2 dummy = ListNode(0)
3 dummy.next = head
4 prev = dummy
5
6 for _ in range(left - 1): # stop just before the reversal region
7 prev = prev.next
8
9 curr = prev.next # permanent tail of the reversed section
10
11 for _ in range(right - left): # relocate right-left nodes to the front, one at a time
12 next_node = curr.next # node about to be moved
13 curr.next = next_node.next # unlink next_node from its current position
14 next_node.next = prev.next # next_node will precede what is currently at the front
15 prev.next = next_node # anchor now points to the newly prepended node
16
17 return dummy.nextTime: O(n) โ one pass to position prev, then O(right โ left) โค O(n) constant-work insertions.
Space: O(1) โ only a fixed number of pointer variables; no auxiliary data structure.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Collect & Overwrite | O(n) | O(n) | When clarity matters more than memory |
| In-Place Insertion | O(n) | O(1) | Interview setting; demonstrates pointer fluency |
Common Mistakes
- Skipping the dummy head: without it,
left = 1leavesprevwith no node before the list to reference; the dummy node eliminates this edge case so both paths are identical. - Wrong inner loop count: the loop runs
right โ lefttimes, notright โ left + 1. The sublist containsright โ left + 1nodes butcurrstays pinned as the tail; only the nodes ahead of it are relocated. - Reading
curr.nexttoo late:next_node = curr.nextmust be captured beforecurr.nextis overwritten on the very next line โ reversing that order permanently loses the reference to the rest of the list. - Advancing
currinside the loop:curris intentionally fixed as the growing tail of the reversed section; moving it forward each step detaches nodes from the chain instead of prepending them. - Off-by-one on the
prevwalk: starting from the dummy node, the loop runs exactlyleft โ 1iterations. After 0 steps you are at the dummy (before position 1); afterleft โ 1steps you are at positionleft โ 1, which is just before the reversal region.
Related Problems
Reverse Linked Listโ reverses the entire list; the pointer-flip mechanics here are a direct prerequisiteReverse Nodes in k-Groupโ applies the same in-place reversal logic to every consecutive k-length chunkSwap Nodes in Pairsโ a specific case of this problem where left=1, right=2, then left=3, right=4, and so onReorder Listโ requires O(1)-space linked list manipulation, including a reversal of the back halfRotate Listโ no reversal, but uses the same dummy-head and positional pointer-walk pattern