92. Reverse Linked List II
Problem Description
You are given the head of a singly linked list and two integers left
and right
where left <= right
. Your task is to reverse the nodes of the linked list from position left
to position right
(positions are 1-indexed), and return the modified list.
For example, if you have a linked list 1 -> 2 -> 3 -> 4 -> 5
and left = 2
, right = 4
, you need to reverse the portion from position 2 to position 4. The result would be 1 -> 4 -> 3 -> 2 -> 5
.
The reversal should only affect the nodes between positions left
and right
(inclusive), while keeping the rest of the list unchanged. The nodes before position left
and after position right
should remain in their original order and properly connected to the reversed portion.
The solution uses a dummy node technique to handle edge cases and performs the reversal by:
- First finding the node just before position
left
(calledpre
) - Then reversing the connections between nodes from position
left
toright
- Finally reconnecting the reversed portion back to the rest of the list
The algorithm tracks pointers p
(pointing to the node before the reversal section) and q
(pointing to the first node in the reversal section), then reverses the required portion and reconnects everything properly.
Intuition
When we need to reverse a portion of a linked list, the key insight is that we need to handle three distinct parts: the nodes before the reversal section, the nodes within the reversal section, and the nodes after the reversal section.
Think of it like rearranging a chain of beads where you only want to reverse a middle section. You need to:
- Remember where the section starts and ends
- Detach the section temporarily
- Reverse the section
- Reattach it back to the chain
The challenge with linked lists is that we can only traverse forward, so we need to carefully track our positions. This leads us to realize we need:
- A pointer to the node just before the reversal starts (to reconnect later)
- A pointer to the first node of the section to be reversed (which will become the last node after reversal)
- A way to reverse the nodes in between
The reversal process itself follows the standard linked list reversal pattern: for each node in the section, we reverse its next
pointer to point to the previous node instead of the next one. We iterate through right - left + 1
nodes to cover the entire section.
Using a dummy node simplifies edge cases, especially when left = 1
(reversing from the head). Without it, we'd need special logic to handle changing the head of the list.
The variables p
and q
serve as anchors: p
marks the node before the reversal section, and q
marks the first node of the reversal section (which becomes the last after reversal). After reversing, we connect p.next
to the new first node of the reversed section and q.next
to the node after the reversed section.
Learn more about Linked List patterns.
Solution Approach
The implementation uses a simulation approach with pointer manipulation to reverse the specified portion of the linked list.
Step 1: Setup dummy node and initial pointer
dummy = ListNode(0, head) pre = dummy
We create a dummy node pointing to head
to handle edge cases uniformly. The pointer pre
will eventually point to the node just before position left
.
Step 2: Navigate to the starting position
for _ in range(left - 1):
pre = pre.next
We move pre
forward left - 1
times to position it at the node just before the reversal section starts.
Step 3: Initialize reversal pointers
p, q = pre, pre.next
cur = q
p
stores the reference to the node before the reversal sectionq
stores the reference to the first node of the reversal section (will become the last after reversal)cur
is our working pointer that traverses through the section to be reversed
Step 4: Reverse the specified section
for _ in range(right - left + 1):
t = cur.next
cur.next = pre
pre, cur = cur, t
This loop reverses right - left + 1
nodes:
t
temporarily stores the next nodecur.next = pre
reverses the current node's pointer to point backwardpre, cur = cur, t
advances both pointers for the next iteration
After this loop:
pre
points to what becomes the new first node of the reversed sectioncur
points to the node immediately after the reversed section
Step 5: Reconnect the reversed section
p.next = pre
q.next = cur
p.next = pre
connects the node before the reversal to the new first node of the reversed sectionq.next = cur
connects the new last node of the reversed section to the rest of the list
Step 6: Return the result
return dummy.next
Returns the head of the modified list, which is dummy.next
.
The time complexity is O(n)
where n
is the number of nodes, as we traverse the list once. The space complexity is O(1)
as we only use a constant amount of extra pointers.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the algorithm with a concrete example:
- Input list:
1 -> 2 -> 3 -> 4 -> 5
left = 2
,right = 4
- Expected output:
1 -> 4 -> 3 -> 2 -> 5
Initial Setup:
dummy -> 1 -> 2 -> 3 -> 4 -> 5 pre = dummy
Step 1: Navigate to position before left
Move pre
forward left - 1 = 1
time:
dummy -> 1 -> 2 -> 3 -> 4 -> 5 ^ pre
Step 2: Set up reversal pointers
dummy -> 1 -> 2 -> 3 -> 4 -> 5 ^ ^ p q,cur
p = pre
(points to node 1)q = pre.next
(points to node 2, first node to reverse)cur = q
(working pointer, starts at node 2)
Step 3: Reverse the section (3 iterations for positions 2-4)
Iteration 1:
- Save
t = cur.next
(node 3) - Set
cur.next = pre
(node 2 now points to node 1) - Move pointers:
pre = cur
(node 2),cur = t
(node 3)
State: 1 <- 2 3 -> 4 -> 5 ^ ^ pre cur
Iteration 2:
- Save
t = cur.next
(node 4) - Set
cur.next = pre
(node 3 now points to node 2) - Move pointers:
pre = cur
(node 3),cur = t
(node 4)
State: 1 <- 2 <- 3 4 -> 5 ^ ^ pre cur
Iteration 3:
- Save
t = cur.next
(node 5) - Set
cur.next = pre
(node 4 now points to node 3) - Move pointers:
pre = cur
(node 4),cur = t
(node 5)
State: 1 <- 2 <- 3 <- 4 5 ^ ^ pre cur
Step 4: Reconnect the reversed section
Remember: p
still points to node 1, q
still points to node 2
p.next = pre
: Connect node 1 to node 4 (new first of reversed section)q.next = cur
: Connect node 2 to node 5 (node after reversed section)
Final: dummy -> 1 -> 4 -> 3 -> 2 -> 5
The portion from position 2 to 4 has been successfully reversed while maintaining all connections!
Solution Implementation
1# Definition for singly-linked list.
2# class ListNode:
3# def __init__(self, val=0, next=None):
4# self.val = val
5# self.next = next
6
7from typing import Optional
8
9class Solution:
10 def reverseBetween(
11 self, head: Optional[ListNode], left: int, right: int
12 ) -> Optional[ListNode]:
13 # Edge cases: single node or no reversal needed
14 if not head or not head.next or left == right:
15 return head
16
17 # Create dummy node to simplify edge cases when left = 1
18 dummy = ListNode(0, head)
19
20 # Find the node just before the reversal section
21 before_reverse = dummy
22 for _ in range(left - 1):
23 before_reverse = before_reverse.next
24
25 # Mark the boundaries of the section to reverse
26 # before_reverse points to node before the reversal section
27 # first_reversed will be the first node in the section (will become last after reversal)
28 first_reversed = before_reverse.next
29
30 # Reverse the nodes between left and right positions
31 prev = before_reverse
32 current = first_reversed
33
34 for _ in range(right - left + 1):
35 # Store next node before breaking the link
36 next_temp = current.next
37 # Reverse the current node's pointer
38 current.next = prev
39 # Move to the next pair of nodes
40 prev = current
41 current = next_temp
42
43 # Connect the reversed section back to the list
44 # prev now points to the new first node of reversed section
45 # current points to the node after the reversed section
46 before_reverse.next = prev
47 first_reversed.next = current
48
49 return dummy.next
50
1/**
2 * Definition for singly-linked list.
3 * public class ListNode {
4 * int val;
5 * ListNode next;
6 * ListNode() {}
7 * ListNode(int val) { this.val = val; }
8 * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
9 * }
10 */
11class Solution {
12 public ListNode reverseBetween(ListNode head, int left, int right) {
13 // Edge case: single node or no reversal needed
14 if (head.next == null || left == right) {
15 return head;
16 }
17
18 // Create dummy node to simplify edge cases when left = 1
19 ListNode dummy = new ListNode(0, head);
20
21 // Find the node just before the reversal section
22 ListNode beforeReverse = dummy;
23 for (int i = 0; i < left - 1; i++) {
24 beforeReverse = beforeReverse.next;
25 }
26
27 // Save important connection points
28 ListNode connectionBeforeReverse = beforeReverse; // Node before the reversed section
29 ListNode firstNodeToReverse = beforeReverse.next; // First node in the section to be reversed
30
31 // Reverse the sublist from position left to right
32 ListNode previous = beforeReverse;
33 ListNode current = firstNodeToReverse;
34
35 for (int i = 0; i < right - left + 1; i++) {
36 // Standard linked list reversal: save next, point back, move forward
37 ListNode nextNode = current.next;
38 current.next = previous;
39 previous = current;
40 current = nextNode;
41 }
42
43 // Reconnect the reversed section with the rest of the list
44 connectionBeforeReverse.next = previous; // Connect to the new head of reversed section
45 firstNodeToReverse.next = current; // Connect the tail of reversed section to remaining list
46
47 return dummy.next;
48 }
49}
50
1/**
2 * Definition for singly-linked list.
3 * struct ListNode {
4 * int val;
5 * ListNode *next;
6 * ListNode() : val(0), next(nullptr) {}
7 * ListNode(int x) : val(x), next(nullptr) {}
8 * ListNode(int x, ListNode *next) : val(x), next(next) {}
9 * };
10 */
11class Solution {
12public:
13 ListNode* reverseBetween(ListNode* head, int left, int right) {
14 // Handle edge cases: single node or no reversal needed
15 if (!head->next || left == right) {
16 return head;
17 }
18
19 // Create dummy node to simplify edge case when left = 1
20 ListNode* dummyNode = new ListNode(0, head);
21
22 // Find the node just before the reversal section
23 ListNode* beforeReverse = dummyNode;
24 for (int i = 0; i < left - 1; ++i) {
25 beforeReverse = beforeReverse->next;
26 }
27
28 // Save important positions:
29 // - nodeBeforeGroup: node just before the reversal section
30 // - firstNodeInGroup: first node that will be reversed (will become last after reversal)
31 ListNode* nodeBeforeGroup = beforeReverse;
32 ListNode* firstNodeInGroup = beforeReverse->next;
33
34 // Reverse the nodes from position left to right
35 ListNode* current = firstNodeInGroup;
36 ListNode* previous = beforeReverse;
37
38 for (int i = 0; i < right - left + 1; ++i) {
39 // Save next node before breaking the link
40 ListNode* nextTemp = current->next;
41
42 // Reverse the current node's pointer
43 current->next = previous;
44
45 // Move pointers forward
46 previous = current;
47 current = nextTemp;
48 }
49
50 // Connect the reversed section back to the list:
51 // - Connect the node before reversal to the new first node (previously last)
52 nodeBeforeGroup->next = previous;
53
54 // - Connect the new last node (previously first) to the remaining list
55 firstNodeInGroup->next = current;
56
57 // Return the actual head (skip dummy node)
58 return dummyNode->next;
59 }
60};
61
1/**
2 * Definition for singly-linked list.
3 * class ListNode {
4 * val: number
5 * next: ListNode | null
6 * constructor(val?: number, next?: ListNode | null) {
7 * this.val = (val===undefined ? 0 : val)
8 * this.next = (next===undefined ? null : next)
9 * }
10 * }
11 */
12
13/**
14 * Reverses a portion of a linked list between positions left and right (1-indexed)
15 * @param head - The head of the linked list
16 * @param left - The starting position for reversal (1-indexed)
17 * @param right - The ending position for reversal (1-indexed)
18 * @returns The head of the modified linked list
19 */
20function reverseBetween(head: ListNode | null, left: number, right: number): ListNode | null {
21 // Calculate the number of nodes to reverse
22 const nodesToReverse: number = right - left;
23
24 // If left equals right, no reversal needed
25 if (nodesToReverse === 0) {
26 return head;
27 }
28
29 // Create a dummy node to simplify edge cases (when left = 1)
30 const dummyNode: ListNode = new ListNode(0, head);
31
32 // Find the node just before the reversal section
33 let previousNode: ListNode | null = null;
34 let currentNode: ListNode = dummyNode;
35
36 // Move to the node just before position 'left'
37 for (let i = 0; i < left; i++) {
38 previousNode = currentNode;
39 currentNode = currentNode.next!;
40 }
41
42 // Store the connection point (node before the reversal section)
43 const connectionPoint: ListNode = previousNode!;
44
45 // Reverse the nodes from position 'left' to 'right'
46 previousNode = null;
47 for (let i = 0; i <= nodesToReverse; i++) {
48 // Store the next node before breaking the link
49 const nextNode: ListNode | null = currentNode.next;
50
51 // Reverse the current node's pointer
52 currentNode.next = previousNode;
53
54 // Move pointers forward
55 previousNode = currentNode;
56 currentNode = nextNode!;
57 }
58
59 // Reconnect the reversed section with the rest of the list
60 // connectionPoint.next is the original first node of the reversed section (now the last)
61 connectionPoint.next!.next = currentNode;
62
63 // Connect the node before reversal to the new first node of reversed section
64 connectionPoint.next = previousNode;
65
66 // Return the actual head (skip dummy node)
67 return dummyNode.next;
68}
69
Time and Space Complexity
Time Complexity: O(n)
The algorithm traverses the linked list at most once. Breaking down the operations:
- The first loop runs
left - 1
times to position thepre
pointer before the reversal section - The second loop runs
right - left + 1
times to reverse the nodes between positionsleft
andright
- Total iterations:
(left - 1) + (right - left + 1) = right
operations
Since right ≤ n
where n
is the length of the linked list, the time complexity is O(n)
.
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space:
- A dummy node to simplify edge cases
- A fixed number of pointer variables (
dummy
,pre
,p
,q
,cur
,t
)
These variables don't scale with the input size, resulting in O(1)
space complexity. The reversal is performed in-place by modifying the existing linked list's pointers without creating any new nodes or using recursion.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Off-by-One Errors in Pointer Navigation
One of the most common mistakes is incorrectly calculating how many steps to move the before_reverse
pointer. Developers often forget that positions are 1-indexed, not 0-indexed.
Incorrect approach:
# Wrong: Moving 'left' times instead of 'left - 1'
for _ in range(left): # This goes too far!
before_reverse = before_reverse.next
Why it fails: If left = 2
, we want before_reverse
to point to the 1st node (the node before position 2). Moving left
times would place it at the 2nd node instead.
Correct approach:
# Correct: Move exactly 'left - 1' times
for _ in range(left - 1):
before_reverse = before_reverse.next
2. Incorrect Loop Count for Reversal
Another frequent error is reversing the wrong number of nodes. The reversal should include both left
and right
positions (inclusive).
Incorrect approach:
# Wrong: Only reverses 'right - left' nodes (missing one)
for _ in range(right - left): # Off by one!
next_temp = current.next
current.next = prev
prev = current
current = next_temp
Why it fails: If reversing from position 2 to 4, we need to reverse 3 nodes (positions 2, 3, and 4), which is right - left + 1 = 4 - 2 + 1 = 3
nodes.
Correct approach:
# Correct: Reverse exactly 'right - left + 1' nodes
for _ in range(right - left + 1):
next_temp = current.next
current.next = prev
prev = current
current = next_temp
3. Creating Cycles When Reconnecting
A subtle but critical error occurs when reconnecting the reversed section. If not done carefully, you can create a cycle in the linked list.
Incorrect approach:
# Wrong: Reconnecting before properly breaking old connections
before_reverse.next = prev
# first_reversed.next still points to its old next node, creating a cycle!
Why it fails: After reversal, first_reversed
(which becomes the last node of the reversed section) still has its next
pointer pointing backward into the reversed section, creating a cycle.
Correct approach:
# Correct: Ensure both connections are updated
before_reverse.next = prev # Connect to new first of reversed section
first_reversed.next = current # Connect last of reversed section to remaining list
4. Not Handling Edge Cases Properly
Failing to handle edge cases when left = 1
(reversing from the head) is common when not using a dummy node.
Incorrect approach without dummy node:
# Wrong: Doesn't handle left = 1 case
before_reverse = None
for i in range(left - 1):
if i == 0:
before_reverse = head
else:
before_reverse = before_reverse.next # Crashes when before_reverse is None!
Correct approach with dummy node:
# Correct: Dummy node elegantly handles all cases
dummy = ListNode(0, head)
before_reverse = dummy
for _ in range(left - 1):
before_reverse = before_reverse.next
# Works even when left = 1
Prevention Tips:
- Always use a dummy node to simplify edge case handling
- Draw diagrams to visualize pointer movements and connections
- Test with minimal examples like
left = 1
,right = n
, andleft = right
- Trace through the algorithm step-by-step with a simple example before coding
- Remember positions are 1-indexed, not 0-indexed, when calculating movements
What data structure does Breadth-first search typically uses to store intermediate states?
Recommended Readings
Linked List Cycle Given a linked list with potentially a loop determine whether the linked list from the first node contains a cycle in it For bonus points do this with constant space Parameters nodes The first node of a linked list with potentially a loop Result Whether there is a loop contained
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!