Problem overview
The method reorderList receives the head of a singly linked list and returns nothing. It must rearrange the nodes in place so that the list reads: first node, last node, second node, second to last node, and so on until every node is used once. Only the links may change. Values stay where they are inside their nodes, and no new nodes are created. The lesson provides the ListNode class, with an int val and a ListNode next.
For 1->2->3->4 the result is 1->4->2->3. For 1->2->3->4->5 it is 1->5->2->4->3: the middle node, 3, is left over and ends the list. For 4->8 nothing moves, because the last node already follows the first.
Approach: split, reverse, interleave
A singly linked list can only be walked forward, so reaching the last node and then the second to last is awkward. Copying the nodes into an array would solve that, but it costs O(n) extra space. The in-place solution has three passes:
- Find the middle and cut the list there.
1->2->3->4->5becomes the first half1->2->3and the second half4->5. - Reverse the second half.
4->5becomes5->4. Now walking the second half from its front visits the original list from the end. - Interleave the halves, taking one node from each in turn:
1->5->2->4->3.
The middle is found with two pointers: slow moves one node at a time and fast moves two. When fast reaches the end, slow has covered half the distance. The loop keeps going only while fast has two more nodes after it, so slow stops on the last node of the first half. For five nodes it stops on the third node (value 3), with fast on the fifth. For four nodes it stops on the second, with fast on the third. When the list has an odd number of nodes, the first half is one node longer, and that extra middle node is the one left over at the end of the reordered list.
Step by step
Find the middle
- Declare
slowandfast, both starting athead. - Loop while
fast.next != null && fast.next.next != null. Both nodes must exist beforefastcan jump two ahead. - Move
slowone node toslow.next. - Move
fasttwo nodes tofast.next.next.
Cut and reverse the second half
- Store
slow.nextinnode. This is the start of the second half. - Set
slow.next = null. The first half must end atslow. Without the cut it would still point into the second half. - Start
prevatnull. It will be the head of the part already reversed, empty at first. - Loop while
node != null, turning one node around per round. - Save
node.nextinnextNode, because the next line overwrites it. - Point
node.nextatprev. That is the reversal. - Advance
prevtonode, thennodetonextNode.
Merge the halves
- Set
firsttoheadandsecondtoprev, the head of the reversed second half. - Loop while
second != null. The second half is never longer than the first. When it runs out, every node is placed, and a leftover first-half node is already at the end. - Save
first.nextandsecond.nextinnextFirstandnextSecond, because both links are about to change. - Set
first.next = second, thensecond.next = nextFirst. A node from the back now sits between two nodes from the front. - Advance
firsttonextFirstandsecondtonextSecond.
The solution in Java
class Solution {
public void reorderList(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
ListNode node = slow.next;
slow.next = null;
ListNode prev = null;
while (node != null) {
ListNode nextNode = node.next;
node.next = prev;
prev = node;
node = nextNode;
}
ListNode first = head;
ListNode second = prev;
while (second != null) {
ListNode nextFirst = first.next;
ListNode nextSecond = second.next;
first.next = second;
second.next = nextFirst;
first = nextFirst;
second = nextSecond;
}
}
}
Java details worth noticing:
- The method returns
void. It works becauseheadis a reference to a node object, and the method changes thenextfields of the nodes that the caller still holds. Reassigning the parameterheadinside the method would not be visible outside, but this solution never does that. The first node stays the first node. - Short-circuit
&&. Infast.next != null && fast.next.next != null, the second test runs only when the first is true. Swapping them would throwNullPointerExceptionon a one-node list. - Temporary variables protect the links. Each overwrite of a
nextfield is preceded by saving the old value in a local (nextNode,nextFirst,nextSecond). Without it, the rest of a half would be unreachable. - No new nodes. Only references move. The solution allocates nothing beyond a handful of local variables.
Complexity
Time: O(n). Finding the middle visits about half of the nodes, reversing visits the second half once, and merging visits each node of the second half once. Each pass is linear.
Space: O(1). The solution uses a fixed number of references and relinks the nodes that already exist, with no recursion and no array copy.
Edge cases and common mistakes
- One node. The loop condition is false at once,
slow.nextisnull, the second half is empty, and nothing changes. The list7stays7. - Two nodes.
4->8stays4->8. The second half8is merged right after4, which is where it already was. - Odd length.
1->2->3becomes1->3->2. The middle node, 2, stays in the first half and ends the list. - Forgetting the cut. If
slow.nextkeeps pointing into the second half, the merged list can loop back on itself, and printing it never ends. - Stopping
slowone node too late. If the loop ran whilefast != null && fast.next != null,slowwould stop on the first node of the second half for even lengths, and the halves would be unbalanced. - Overwriting a link before saving it. Setting
node.next = prevbefore savingnode.nextloses the rest of the half. - Repeated values. Values may repeat, for instance six nodes that all hold 5. The algorithm moves nodes by position, not by value, so the list stays correct.
- Changing values instead of links. Swapping the
valfields would break the rule that only links may change.
Testing
1->2->3->4becomes1->4->2->3.1->2->3->4->5becomes1->5->2->4->3.4->8stays4->8.7stays7.1->2->3becomes1->3->2.5->5->5->5->5->5stays the same.1->2->3->4->5->6->7->8becomes1->8->2->7->3->6->4->5.
After the first two passes on 1->2->3->4->5, the halves are 1->2->3 and 5->4, ready to interleave.
