A detailed guide to solving Reverse Linked List II with a dummy node and in-place sublist reversal.
Problem Restatement
We are given the head of a singly linked list and two integers:
left
rightThe positions are 1-indexed.
We need to reverse the nodes from position left to position right, inclusive, and return the modified list.
Only that middle section should be reversed. The nodes before left and after right must stay in their original order.
The official statement asks us to reverse the nodes from position left to position right and return the reversed list. Example: [1,2,3,4,5], left = 2, right = 4 becomes [1,4,3,2,5].
Input and Output
| Item | Meaning |
|---|---|
| Input | Head of a singly linked list, integer left, integer right |
| Output | Head of the modified linked list |
| Position rule | Positions are 1-indexed |
| Reversal range | Reverse nodes from left through right |
| Outside range | Keep all other nodes in the same order |
Function shape:
class Solution:
def reverseBetween(
self,
head: Optional[ListNode],
left: int,
right: int,
) -> Optional[ListNode]:
...Examples
Example 1:
head = [1, 2, 3, 4, 5]
left = 2
right = 4The section to reverse is:
2 -> 3 -> 4After reversal:
4 -> 3 -> 2The final list is:
[1, 4, 3, 2, 5]Example 2:
head = [5]
left = 1
right = 1There is only one node, so reversing the range changes nothing.
Answer:
[5]First Thought: Copy Values Into an Array
A simple method is to copy all node values into an array.
Then reverse the slice:
values[left - 1:right]Then write the values back into the linked list.
This works, but it does not reverse the linked list nodes directly. It also uses extra memory.
We can solve it in-place by rewiring next pointers.
Key Insight
The list has three parts:
before left
left ... right
after rightOnly the middle part changes direction.
A dummy node helps handle the case where left = 1.
dummy -> headWe first move a pointer prev to the node just before position left.
Then we reverse the next right - left + 1 nodes.
Finally, we reconnect the reversed section to the rest of the list.
Algorithm
Create:
dummy = ListNode(0, head)Move prev to the node before the reversed section:
prev = dummy
for _ in range(left - 1):
prev = prev.nextNow:
| Pointer | Meaning |
|---|---|
prev | Node before the reversed section |
cur | First node inside the section |
tail | The first node inside the section, which becomes the tail after reversal |
Reverse exactly right - left + 1 nodes.
After reversal:
prev.nextshould point to the new head of the reversed section.tail.nextshould point to the node after the reversed section.- Return
dummy.next.
Walkthrough
Use:
head = [1, 2, 3, 4, 5]
left = 2
right = 4Add dummy:
dummy -> 1 -> 2 -> 3 -> 4 -> 5Move prev to the node before position 2:
prev = node 1The section starts at node 2.
prev -> 2 -> 3 -> 4 -> 5Now reverse three nodes:
2 -> 3 -> 4During reversal, the links become:
4 -> 3 -> 2The node 2 becomes the tail of the reversed section.
Reconnect:
1 -> 4 -> 3 -> 2 -> 5Return:
[1, 4, 3, 2, 5]Correctness
The algorithm first places prev immediately before position left. Therefore, prev.next is the first node that must be reversed.
The reversal loop runs exactly right - left + 1 times, so it reverses exactly the nodes in the target range and no others.
During the reversal, cur moves through the target section, and new_head becomes the head of the reversed section. The original first node of the section is saved as tail; after reversal, that node becomes the last node of the reversed section.
The node after the reversed range is saved as cur when the reversal loop finishes. Setting:
tail.next = curconnects the reversed section to the suffix of the original list.
Setting:
prev.next = new_headconnects the prefix of the original list to the reversed section.
All nodes before left are untouched. All nodes after right are untouched. The middle section is reversed. Therefore, the returned list is correct.
Complexity
Let:
n = number of nodes| Metric | Value | Why |
|---|---|---|
| Time | O(n) | We move through the list at most once |
| Space | O(1) | Only pointers are used |
Implementation
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseBetween(
self,
head: Optional[ListNode],
left: int,
right: int,
) -> Optional[ListNode]:
dummy = ListNode(0, head)
prev = dummy
for _ in range(left - 1):
prev = prev.next
tail = prev.next
cur = prev.next
new_head = None
for _ in range(right - left + 1):
nxt = cur.next
cur.next = new_head
new_head = cur
cur = nxt
prev.next = new_head
tail.next = cur
return dummy.nextCode Explanation
The dummy node handles reversal starting at the head:
dummy = ListNode(0, head)Move prev to the node before left:
prev = dummy
for _ in range(left - 1):
prev = prev.nextSave the first node of the section:
tail = prev.nextThis node becomes the tail after reversal.
Start reversing from the same node:
cur = prev.next
new_head = NoneReverse exactly the target section:
for _ in range(right - left + 1):
nxt = cur.next
cur.next = new_head
new_head = cur
cur = nxtAfter the loop:
| Pointer | Meaning |
|---|---|
new_head | Head of the reversed section |
tail | Tail of the reversed section |
cur | First node after the reversed section |
Reconnect the prefix:
prev.next = new_headReconnect the suffix:
tail.next = curReturn the real head:
return dummy.nextTesting
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def build_list(values):
dummy = ListNode()
cur = dummy
for value in values:
cur.next = ListNode(value)
cur = cur.next
return dummy.next
def to_list(head):
values = []
while head:
values.append(head.val)
head = head.next
return values
def check(values, left, right, expected):
s = Solution()
head = build_list(values)
result = s.reverseBetween(head, left, right)
assert to_list(result) == expected
def run_tests():
check([1, 2, 3, 4, 5], 2, 4, [1, 4, 3, 2, 5])
check([5], 1, 1, [5])
check([1, 2, 3], 1, 2, [2, 1, 3])
check([1, 2, 3], 2, 3, [1, 3, 2])
check([1, 2, 3], 1, 3, [3, 2, 1])
check([1, 2], 1, 1, [1, 2])
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
[1, 2, 3, 4, 5], 2..4 | Main example |
[5], 1..1 | Single node |
| Reverse from head | Checks dummy node logic |
| Reverse to tail | Checks suffix reconnect |
| Reverse whole list | Checks both head and tail changes |
left == right | No effective change |