Reorder List
Split the list in the middle, reverse the second half, then weave the two halves together. Solved two ways, an array of nodes in O(n) time and O(n) space, and in place in O(n) time and O(1) space, with a step-through, code in five languages and the Big O worked out from the code.
The problem
Section titled “The problem”You are given the head of a singly linked list with nodes L0, L1, …, Ln−1. Reorder it to:
L0, Ln−1, L1, Ln−2, L2, …
So the first node, then the last, then the second, then the second to last, and so on. Change the links between nodes, not the values inside them. Return nothing: change the list in place.
Example 1
Input: head = [10, 20, 30, 40, 50, 60]
Output: [10, 60, 20, 50, 30, 40]
Front and back take turns. The top row is the first half, the bottom row is the second half.
Example 2
Input: head = [7, 3, 9, 1, 5]
Output: [7, 5, 3, 1, 9]
With an odd count, the middle node, 9, ends up last.
Example 3
Input: head = [4]
Output: [4]
One node has nothing to reorder.
Constraints
Section titled “Constraints”- 1 ≤ n ≤ 50,000, where n is the number of nodes
- 1 ≤ Node.val ≤ 1,000
Clarifying questions
Section titled “Clarifying questions”- May we just swap the values?
- No. The nodes themselves must move, by changing their next pointers.
- Do we return a new head?
- No. The head stays the same node, so the function returns nothing.
- Can the list be empty?
- No, it has at least one node. One or two nodes are already in order.
- Is extra memory allowed?
- An array of the nodes works. The best answer uses only a few pointers.
Spot the pattern
Section titled “Spot the pattern”The order needs nodes from both ends at once, but a singly linked list only walks forward. Two tricks fix that. Fast and slow pointers find the middle in one pass. Reversing the second half makes its last node come first. After that it is a plain merge of two lists, taking turns.
Solutions
Section titled “Solutions”A slower way with no extra memory walks to the end of the list for every node, which is O(n²). The array below is the simple version worth knowing.
Array of nodes O(n), O(n) space
The idea
Section titled “The idea”The hard part is reaching the back of the list. So copy every node into an array. Now index j
reaches any node from the back in one step.
Put i at the front and j at the back. Link nodes[i] to nodes[j], then nodes[j] to
nodes[i + 1], and move both inward. When they meet, that node is the new tail. Set its next to
null, or the list would loop.
Walkthrough
Section titled “Walkthrough”- nodes = [every node, in order]
- i, j = 0, len(nodes) - 1
- while i < j:
- nodes[i].next = nodes[j]
- i += 1
- if i == j: break
- nodes[j].next = nodes[i]
- j -= 1
- nodes[i].next = None
Copy every node into an array. Now any node can be reached by its index in one step, from either end.
class Solution { // Array of nodes: index j reaches the back in one step. Link front and back // in turns. O(n) time, but the array costs O(n) memory. public void reorderList(ListNode head) { java.util.List<ListNode> nodes = new java.util.ArrayList<>(); for (ListNode node = head; node != null; node = node.next) nodes.add(node); int i = 0, j = nodes.size() - 1; while (i < j) { nodes.get(i).next = nodes.get(j); i++; if (i == j) break; nodes.get(j).next = nodes.get(i); j--; } // The meeting node is the new tail. Cut its old link, or the list loops. nodes.get(i).next = null; }}# Array of nodes: index j reaches the back in one step. Link front and back# in turns. O(n) time, but the array costs O(n) memory.def reorder_list(head: ListNode) -> None: nodes = [] node = head while node: nodes.append(node) node = node.next i, j = 0, len(nodes) - 1 while i < j: nodes[i].next = nodes[j] i += 1 if i == j: break nodes[j].next = nodes[i] j -= 1 # The meeting node is the new tail. Cut its old link, or the list loops. nodes[i].next = Nonepublic class Solution{ // Array of nodes: index j reaches the back in one step. Link front and back // in turns. O(n) time, but the array costs O(n) memory. public void ReorderList(ListNode head) { var nodes = new System.Collections.Generic.List<ListNode>(); for (var node = head; node != null; node = node.next) nodes.Add(node); int i = 0, j = nodes.Count - 1; while (i < j) { nodes[i].next = nodes[j]; i++; if (i == j) break; nodes[j].next = nodes[i]; j--; } // The meeting node is the new tail. Cut its old link, or the list loops. nodes[i].next = null; }}// Array of nodes: index j reaches the back in one step. Link front and back// in turns. O(n) time, but the array costs O(n) memory.func reorderList(head *ListNode) { nodes := []*ListNode{} for node := head; node != nil; node = node.Next { nodes = append(nodes, node) } i, j := 0, len(nodes)-1 for i < j { nodes[i].Next = nodes[j] i++ if i == j { break } nodes[j].Next = nodes[i] j-- } // The meeting node is the new tail. Cut its old link, or the list loops. nodes[i].Next = nil}// Array of nodes: index j reaches the back in one step. Link front and back// in turns. O(n) time, but the array costs O(n) memory.function reorderList(head: ListNode | null): void { const nodes: ListNode[] = []; for (let node = head; node; node = node.next) nodes.push(node); let i = 0; let j = nodes.length - 1; while (i < j) { nodes[i].next = nodes[j]; i++; if (i === j) break; nodes[j].next = nodes[i]; j--; } // The meeting node is the new tail. Cut its old link, or the list loops. nodes[i].next = null;}1 Find the work that repeats
Two parts repeat. Copying adds one node to the array per run. The linking loop sets one
nextpointer and moves one index per link. Each of those is 1 step.- nodes = [every node, in order]
- i, j = 0, len(nodes) - 1
- while i < j:
- nodes[i].next = nodes[j]
- i += 1
- if i == j: break
- nodes[j].next = nodes[i]
- j -= 1
- nodes[i].next = None
2 Count the runs on Example 1
Example 1 has 6 nodes. Copying takes 6 runs. Linking makes 5 links: 10 to 60, 60 to 20, 20 to 50, 50 to 30 and 30 to 40.
6 + 5 = 11 steps.
part copy nodes link runs 6 5 total6 + 5 = 113 Write the count for any n
The copy touches each node once. Each link moves
iorjone step closer, and they start n − 1 apart:n + (n − 1) = 2n − 1Check it with Example 1: 2 × 6 − 1 = 11.
4 Keep only the part that grows fastest
Drop the 2 and the − 1. What is left is n.
So the time is O(n).
S Space: count the extra memory the same way
The array holds a reference to every node, so n slots. The space is O(n).
nodes (n slots)ij
Split, reverse, weave O(n), O(1) space
The idea
Section titled “The idea”Do the job in three steps, with no array.
- Find the middle. Move
slow1 node andfast2 nodes per run. Whenfastcannot go 2 more,slowis at the end of the first half. Cut the list after it. - Reverse the second half. Now its last node comes first, which is the order we need.
- Weave. Take one node from the first half, then one from the reversed second half, and repeat.
Why it is correct: after step 2, the second half lists Ln−1, Ln−2, … in that order. Weaving it with L0, L1, … gives exactly L0, Ln−1, L1, Ln−2, …. The first half is never shorter, so no node is left behind.
Walkthrough
Section titled “Walkthrough”- slow = fast = head
- while fast.next and fast.next.next:
- slow, fast = slow.next, fast.next.next
- second, slow.next = slow.next, None
- previous = None
- while second:
- second.next, previous, second = previous, second, second.next
- first, second = head, previous
- while second:
- first_next, second_next = first.next, second.next
- first.next, second.next = second, first_next
- first, second = first_next, second_next
Step 1: find the middle. slow moves 1 node per run and fast moves 2, so slow stops halfway.
class Solution { // Split, reverse, weave: find the middle, reverse the second half, then // take one node from each half in turn. O(n) time and O(1) memory. public void reorderList(ListNode head) { // fast moves 2 per run, so slow stops at the end of the first half. ListNode slow = head, fast = head; while (fast.next != null && fast.next.next != null) { slow = slow.next; fast = fast.next.next; } // Cut the halves apart, or the weave would make a loop. ListNode second = slow.next; slow.next = null; // Reverse the second half, so its last node comes first. ListNode previous = null; while (second != null) { ListNode nextNode = second.next; second.next = previous; previous = second; second = nextNode; } // The first half is never shorter, so stop when the second runs out. ListNode first = head; second = previous; while (second != null) { ListNode firstNext = first.next, secondNext = second.next; first.next = second; second.next = firstNext; first = firstNext; second = secondNext; } }}# Split, reverse, weave: find the middle, reverse the second half, then# take one node from each half in turn. O(n) time and O(1) memory.def reorder_list(head: ListNode) -> None: # fast moves 2 per run, so slow stops at the end of the first half. slow = fast = head while fast.next and fast.next.next: slow, fast = slow.next, fast.next.next # Cut the halves apart, or the weave would make a loop. second, slow.next = slow.next, None # Reverse the second half, so its last node comes first. previous = None while second: second.next, previous, second = previous, second, second.next # The first half is never shorter, so stop when the second runs out. first, second = head, previous while second: first_next, second_next = first.next, second.next first.next, second.next = second, first_next first, second = first_next, second_nextpublic class Solution{ // Split, reverse, weave: find the middle, reverse the second half, then // take one node from each half in turn. O(n) time and O(1) memory. public void ReorderList(ListNode head) { // fast moves 2 per run, so slow stops at the end of the first half. ListNode slow = head, fast = head; while (fast.next != null && fast.next.next != null) { slow = slow.next; fast = fast.next.next; } // Cut the halves apart, or the weave would make a loop. ListNode second = slow.next; slow.next = null; // Reverse the second half, so its last node comes first. ListNode previous = null; while (second != null) { ListNode nextNode = second.next; second.next = previous; previous = second; second = nextNode; } // The first half is never shorter, so stop when the second runs out. ListNode first = head; second = previous; while (second != null) { ListNode firstNext = first.next, secondNext = second.next; first.next = second; second.next = firstNext; first = firstNext; second = secondNext; } }}// Split, reverse, weave: find the middle, reverse the second half, then// take one node from each half in turn. O(n) time and O(1) memory.func reorderList(head *ListNode) { // fast moves 2 per run, so slow stops at the end of the first half. slow, fast := head, head for fast.Next != nil && fast.Next.Next != nil { slow, fast = slow.Next, fast.Next.Next } // Cut the halves apart, or the weave would make a loop. second := slow.Next slow.Next = nil // Reverse the second half, so its last node comes first. var previous *ListNode for second != nil { second.Next, previous, second = previous, second, second.Next } // The first half is never shorter, so stop when the second runs out. first := head second = previous for second != nil { firstNext, secondNext := first.Next, second.Next first.Next, second.Next = second, firstNext first, second = firstNext, secondNext }}// Split, reverse, weave: find the middle, reverse the second half, then// take one node from each half in turn. O(n) time and O(1) memory.function reorderList(head: ListNode | null): void { if (!head) return; // fast moves 2 per run, so slow stops at the end of the first half. let slow = head; let fast = head; while (fast.next && fast.next.next) { slow = slow.next!; fast = fast.next.next; } // Cut the halves apart, or the weave would make a loop. let second = slow.next; slow.next = null; // Reverse the second half, so its last node comes first. let previous: ListNode | null = null; while (second) { const nextNode: ListNode | null = second.next; second.next = previous; previous = second; second = nextNode; } // The first half is never shorter, so stop when the second runs out. let first: ListNode | null = head; second = previous; while (second && first) { const firstNext: ListNode | null = first.next; const secondNext: ListNode | null = second.next; first.next = second; second.next = firstNext; first = firstNext; second = secondNext; }}1 Find the work that repeats
There are three loops, one after the other. Each run of each loop moves a few pointers, which is 1 step. None of them sits inside another.
- slow = fast = head
- while fast.next and fast.next.next:
- slow, fast = slow.next, fast.next.next
- second, slow.next = slow.next, None
- previous = None
- while second:
- second.next, previous, second = previous, second, second.next
- first, second = head, previous
- while second:
- first_next, second_next = first.next, second.next
- first.next, second.next = second, first_next
- first, second = first_next, second_next
2 Count the runs on Example 1
Example 1 has 6 nodes. Finding the middle takes 2 runs. Reversing the 3 nodes of the second half takes 3 runs. Weaving those 3 nodes into the first half takes 3 runs.
2 + 3 + 3 = 8 runs.
loop find middle reverse weave runs 2 3 3 total2 + 3 + 3 = 83 Write the count for any n
Fast moves 2 nodes per run, so finding the middle takes about n / 2 runs. The second half has about n / 2 nodes, and both the reverse and the weave touch each of them once:
about n/2 + n/2 + n/2 = 3n/2Check it with Example 1: 3 × 6 / 2 = 9, and the real count is 8 because the middle search stops one run early.
4 Keep only the part that grows fastest
Drop the 3/2. Three half-passes do not change how the work grows. What is left is n.
So the time is O(n).
S Space: count the extra memory the same way
The code keeps a handful of pointers:
slow,fast,previous,first,second,first_nextandsecond_next. That is fixed, for any n, so the space is O(1).slowfastpreviousfirstsecondfirst_nextsecond_next
Tested at the limits
Section titled “Tested at the limits”2,000 random lists of 1 to 9 nodes, with odd and even lengths and single nodes. The array of nodes and split, reverse, weave gave the same order every time, and it matched a simple check that takes from the front and the back in turns.
| Largest inputs | Result |
|---|---|
| 50,000 nodes (the most allowed). Split, reverse, weave. | 0.002 s, and under 0.001 MB of extra memory: a few pointers, whatever the length. |
| 50,000 nodes, the same list. Array of nodes. | 0.003 s, and about 0.4 MB of extra memory: the array holds every node, which is the O(n) space. |
The designbeyondcode digest, every Saturday
New deep dives, interview tips and site news in one short email. Free. Leave with one click.
We never sell or share your email. We only use it to send this newsletter.