Reverse Linked List II
Difficulty: Medium Source: NeetCode
Problem
Given the head of a singly linked list and two integers
leftandrightwhereleft <= right, reverse the nodes of the list from positionleftto positionright, and return the reversed list. Positions are 1-indexed.Example 1: Input: head = [1,2,3,4,5], left = 2, right = 4 Output: [1,4,3,2,5]
Example 2: Input: head = [5], left = 1, right = 1 Output: [5]
Constraints:
- The number of nodes in the list is
n- 1 <= n <= 500
- -500 <= Node.val <= 500
- 1 <= left <= right <= n
Prerequisites
Before attempting this problem, you should be comfortable with:
- Reversing a Linked List — in-place pointer reversal with prev/curr (see problem 1)
- Dummy Head Trick — sentinel node to handle edge cases like reversing from position 1
- Pointer Re-attachment — connecting reversed sublist back to the surrounding nodes
1. Brute Force — Collect and Rebuild
Intuition
Extract the values in the subrange, reverse them, then put them back. Not the most elegant, but easy to reason about and good for verifying the optimal approach.
Algorithm
- Walk the list and collect all node values into a Python list.
- Reverse the slice
values[left-1:right]in-place. - Walk the original list again and overwrite each node’s
.valwith the new values.
Solution
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def build_list(values):
if not values:
return None
head = ListNode(values[0])
cur = head
for v in values[1:]:
cur.next = ListNode(v)
cur = cur.next
return head
def print_list(head):
result = []
while head:
result.append(str(head.val))
head = head.next
print(" -> ".join(result) if result else "None")
def reverseBetween_brute(head, left, right):
values = []
cur = head
while cur:
values.append(cur.val)
cur = cur.next
# Reverse the slice [left-1, right) in-place
l, r = left - 1, right - 1
while l < r:
values[l], values[r] = values[r], values[l]
l += 1
r -= 1
# Write values back into nodes
cur = head
for v in values:
cur.val = v
cur = cur.next
return head
# Test cases
head = build_list([1, 2, 3, 4, 5])
print("Output: ", end=""); print_list(reverseBetween_brute(head, 2, 4)) # 1->4->3->2->5
head2 = build_list([5])
print("Output: ", end=""); print_list(reverseBetween_brute(head2, 1, 1)) # 5
head3 = build_list([1, 2, 3, 4, 5])
print("Output: ", end=""); print_list(reverseBetween_brute(head3, 1, 5)) # 5->4->3->2->1
Complexity
- Time:
O(n)— two passes - Space:
O(n)— storing all values
2. In-Place with Dummy Head (Optimal)
Intuition
We want to reverse exactly the nodes from position left to right without touching anything else. The key is to identify four anchor points:
prev_left: the node just before positionleft(we’ll re-attach the reversed segment here)- The node at position
left(it becomes the tail of the reversed segment) - The node at position
right(it becomes the new head of the reversed segment) - The node just after position
right(we’ll re-attach to the tail of the reversed segment)
Using a dummy head means prev_left always exists, even when left = 1.
Example: [1, 2, 3, 4, 5], left=2, right=4
dummy -> 1 -> 2 -> 3 -> 4 -> 5 -> None
^ ^ ^ ^
prev_l left_node right after_right
(pos 1) (pos 2) (pos 4) (pos 5)
After reversing positions 2-4:
dummy -> 1 -> 4 -> 3 -> 2 -> 5 -> None
^ ^
left_node after_right
(now tail)
Algorithm
- Create a
dummynode and setdummy.next = head. - Walk
left - 1steps fromdummyto findprev_left(the node before positionleft). - Set
left_node = prev_left.next(the node at positionleft). - Reverse the sublist from position
lefttorightusing standard iterative reversal. Keep count. - Re-attach:
prev_left.next = right_node(the new head of the reversed segment) andleft_node.next = after_right. - Return
dummy.next.
Solution
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def build_list(values):
if not values:
return None
head = ListNode(values[0])
cur = head
for v in values[1:]:
cur.next = ListNode(v)
cur = cur.next
return head
def print_list(head):
result = []
while head:
result.append(str(head.val))
head = head.next
print(" -> ".join(result) if result else "None")
def reverseBetween(head, left, right):
dummy = ListNode(0, head)
# Step 1: Walk to the node just before position 'left'
prev_left = dummy
for _ in range(left - 1):
prev_left = prev_left.next
# Step 2: 'left_node' is the node at position 'left'
# It will become the tail of the reversed segment
left_node = prev_left.next
# Step 3: Reverse from position left to right
# We reverse (right - left) times
prev = None
cur = left_node
for _ in range(right - left + 1):
next_node = cur.next
cur.next = prev
prev = cur
cur = next_node
# After loop:
# prev = node at position 'right' (new head of reversed segment)
# cur = node at position 'right+1' (first node after the segment)
# Step 4: Re-attach
prev_left.next = prev # connect node before segment to new head
left_node.next = cur # connect old left (now tail) to node after segment
return dummy.next
# Test cases
head = build_list([1, 2, 3, 4, 5])
print("Output: ", end=""); print_list(reverseBetween(head, 2, 4)) # 1->4->3->2->5
head2 = build_list([5])
print("Output: ", end=""); print_list(reverseBetween(head2, 1, 1)) # 5
# left=1: reverse from the very start (dummy saves us here)
head3 = build_list([1, 2, 3, 4, 5])
print("Output: ", end=""); print_list(reverseBetween(head3, 1, 5)) # 5->4->3->2->1
head4 = build_list([3, 5])
print("Output: ", end=""); print_list(reverseBetween(head4, 1, 2)) # 5->3
Complexity
- Time:
O(n)— at most two passes: one to findprev_left, one to reverse - Space:
O(1)— only pointer variables
Common Pitfalls
Not using a dummy node when left = 1. If the reversal starts at the head, there’s no “node before position left”. The dummy node acts as a fake predecessor so the re-attachment code stays uniform.
Getting confused about prev and cur after the reversal loop. After reversing right - left + 1 nodes, prev points to the node that was at position right (now the start of the reversed segment) and cur points to the node that was at position right + 1 (the first node after the segment). Keep this straight for the re-attachment.
Forgetting to re-attach left_node.next. After reversal, left_node (original position-left node) has next = None (or whatever prev started as). You must set left_node.next = cur to reconnect it to the rest of the list.