An editorial queue is a singly linked list of story nodes. The night editor wants positions left through right flipped — that cluster of follow-ups, inverted in place — without allocating a second chain of story objects. The intern copied values in the range into an ArrayList, reversed the array, and wrote them back. The order looked reversed. The nodes were not. Preview widgets that still held a node from the old slice were following the same successor they always had — only the numbers painted on those nodes had moved.
Reverse Linked List II rewires a contiguous sublist, inclusive, and returns the (possibly new) head. The linked list post owns dummy nodes and splice. Reverse Linked List owns the three-pointer rewire. Here we only care about a handle on the node before left, a bounded reverse, and two links that put the reversed run back into the list.
The problem
Given the head of a singly linked list and 1-indexed positions left and right (left <= right, always in range), reverse the nodes from left through right inclusive and return the head. left == right is a no-op. left may be 1 — the reversed run starts at the old head, so the returned head can be a different node.
1 → 2 → 3 → 4 → 5, left=2, right=4 → 1 → 4 → 3 → 2 → 5
1 → 2 → 3, left=1, right=2 → 2 → 1 → 3
5, left=1, right=1 → 5
A node is the usual two fields:
final class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
}
Note: In-place means the same node objects, new next links on that slice. Copying values in [left, right] into an array, reversing the array, and writing them back passes a value-order test. The node that sat at right is still at right, now wearing a different number. Reverse Linked List named the identity trap when the intern built a second chain; here you painted over the original one.
Copy the range, reverse the array
Walk to position left, collect right - left + 1 values, reverse the collection, write them back onto those same nodes. Head never moves. Correct on values. Wrong on identity.
ListNode reverseBetweenByValues(ListNode head, int left, int right) {
ListNode p = head;
for (int i = 1; i < left; i++) {
p = p.next;
}
ListNode start = p;
List<Integer> vals = new ArrayList<>();
for (int i = left; i <= right; i++) {
vals.add(p.val);
p = p.next;
}
Collections.reverse(vals);
p = start;
for (int v : vals) {
p.val = v;
p = p.next;
}
return head;
}
Every listener still holding a node thinks the successors never changed — because they did not. You paid O(k) extra space for a question the list already answers by rewiring next.
Dummy, reverse the run, splice it back
A dummy in front of head gives every sublist — including one that starts at the old head — a predecessor, so left = 1 is not a special case. Walk left - 1 steps from dummy; before now sits on the node before the run. The run’s first node is before.next. That node will become the tail of the reversed slice.
Reverse the next right - left + 1 nodes with the same three pointers as Reverse Linked List — prev, current, next, same four lines, stop after that many steps. Do not re-derive the assignment order here. After the loop:
previs the new head of the reversed run (old positionright).currentis the first node after the run (ornullifrightwas the tail).tail(before.nextfrom before the loop) is the old left node, now the tail of the reversed run, and it currently points atnull.
Splice: before.next = prev, then tail.next = current. Return dummy.next.
Walk left = 2, right = 4 on 1 → 2 → 3 → 4 → 5:
dummy → 1 → 2 → 3 → 4 → 5 → null left=2 right=4
walk left-1 = 1 step from dummy:
before=1, tail=2, run length=3
reverse 2 → 3 → 4 (same three pointers, three steps):
prev=4 → 3 → 2 → null
current=5
before.next still 2
splice:
before.next = prev 1 → 4 → 3 → 2 → null
tail.next = current 2 → 5
dummy → 1 → 4 → 3 → 2 → 5 → null
return dummy.next
When left = 1, before stays on dummy. After the splice, dummy.next is the new head. Returning the original head would hand back the tail of the reversed prefix.
ListNode reverseBetween(ListNode head, int left, int right) {
if (left == right) {
return head;
}
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode before = dummy;
for (int i = 1; i < left; i++) {
before = before.next;
}
ListNode tail = before.next;
ListNode prev = null;
ListNode current = tail;
for (int i = 0; i < right - left + 1; i++) {
ListNode next = current.next;
current.next = prev;
prev = current;
current = next;
}
before.next = prev;
tail.next = current;
return dummy.next;
}
Time is O(n) — walk to left, then one reverse of the slice; every node is touched at most once. Space is O(1) besides the dummy and the three pointers.
Forgetting tail.next = current drops the suffix on the floor. After the bounded reverse, the old left node points at null. The splice is the whole difference between “reversed a prefix of a detached chain” and “reversed a sublist.” And when left = 1, return dummy.next, not the original head.
left == right is already reversed. The early return is optional — one reverse step plus splice is a no-op if you keep the handles — but it makes the no-op obvious at the board.
What interviewers usually poke next
left = 1andright = n. The whole list. Same splice; dummy still works. You should be able to say this is Reverse Linked List with a dummy you did not need there.- Reverse nodes in k-group. Same bounded reverse and splice, repeated until the leftover is shorter than
k. That is Reverse Nodes in k-Group — name the handles, do not start writing it unless they ask. - Doubly linked. You also rewire
prev. The idea is the same; the bug surface doubles. The layout post already has that variant. - Do it by swapping values. Correct for value-order tests,
O(k)extra space, and you should say why in-place rewiring is the list they actually handed you.
You are done with this problem when you can reverse 2..4 on 1 → 2 → 3 → 4 → 5 without losing node 5, and you can name why a dummy makes left = 1 ordinary.