Problem
Given a singly linked list, rearrange it so that all nodes at odd positions appear first, followed by all nodes at even positions. Positions are 1-indexed โ the first node is at position 1, the second at position 2, and so on. The relative order within each group must be preserved.
[1,2,3,4,5]
- Input:
head = [1, 2, 3, 4, 5] - Output:
[1, 3, 5, 2, 4] - Explanation: Nodes at positions 1, 3, 5 (values 1, 3, 5) come first, then nodes at positions 2, 4 (values 2, 4).
A counter-example: [2, 1, 3, 5, 6, 4, 7] โ [2, 3, 6, 7, 1, 5, 4]. Notice that the node with value 2 (at position 1) stays in the "odd" group even though 2 is an even number โ it's the position, not the value, that determines the group.
Intuition
The problem asks you to interleave two sequences โ odd-indexed and even-indexed nodes โ back-to-back. The key insight is that you never actually need to move nodes to new memory: you can maintain two running tails simultaneously, advancing each by two steps on every iteration, then stitch the chains together once. The only bookkeeping required is saving a reference to where the even sublist starts before any pointers are modified.
Approach 1 โ Collect and Rebuild
Walk the list once, separating node values by index parity into two arrays, then overwrite the original list nodes with values in the new order.
- Walk the list with an index counter, appending each node's value to either
odd_valuesoreven_valuesbased on whether the index is odd or even. - Walk the list a second time, overwriting each node's value from the concatenated
odd_values + even_valuesarray. - Return the original head (structure unchanged, values reordered).
1def oddEvenList(self, head: Optional[ListNode]) -> Optional[ListNode]:
2 if not head:
3 return head
4
5 odd_values = []
6 even_values = []
7 current = head
8 index = 1
9 while current:
10 if index % 2 == 1:
11 odd_values.append(current.val)
12 else:
13 even_values.append(current.val)
14 current = current.next
15 index += 1
16
17 current = head
18 for val in odd_values + even_values:
19 current.val = val
20 current = current.next
21
22 return headTime: O(n) โ two full passes through the list
Space: O(n) โ two arrays that together hold every node's value
Approach 2 โ In-Place Two Pointers
Maintain two running tails โ odd for the odd-indexed sublist and even for the even-indexed sublist โ advancing both by two positions each iteration, then connect the odd tail to the saved even head.
- Save
even_head = head.nextimmediately โ this is the entry point to the even sublist and must be captured before any pointer is modified. - Set
odd = headandeven = head.nextas the current tails of each sublist. - While
evenandeven.nextare both non-null:- Link
odd.nexttoeven.next(the next odd-indexed node). - Advance
oddtoodd.next. - Link
even.nexttoodd.next(the next even-indexed node). - Advance
eventoeven.next.
- Link
- Set
odd.next = even_headto attach the complete even sublist after the odd sublist. - Return the original head.
1def oddEvenList(self, head: Optional[ListNode]) -> Optional[ListNode]:
2 if not head:
3 return head
4
5 odd = head # running tail of the odd-indexed sublist
6 even = head.next # running tail of the even-indexed sublist
7 even_head = even # save even list entry; needed to reconnect at the end
8
9 while even and even.next:
10 odd.next = even.next # skip over even node to reach the next odd node
11 odd = odd.next # advance odd tail forward by two positions
12 even.next = odd.next # skip over odd node to reach the next even node
13 even = even.next # advance even tail forward by two positions
14
15 odd.next = even_head # attach the complete even sublist after the odd sublist
16 return headTime: O(n) โ single pass; each node is touched exactly once
Space: O(1) โ only three extra pointers regardless of list length
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Collect and Rebuild | O(n) | O(n) | When clarity matters more than memory and you're allowed to copy values |
| In-Place Two Pointers | O(n) | O(1) | Preferred; whenever pointer-level rearrangement is required |
Common Mistakes
- Forgetting to save
even_headbefore modifying pointers โ the first line inside the loop re-linksodd.nextaway from the even sublist, so if you didn't captureeven_headfirst, you've permanently lost the entry point to the even chain. - Loop condition missing
even.nextโ writingwhile even:instead ofwhile even and even.next:causes a null dereference when accessingeven.nextinside the loop on any list with an even number of elements, where the final even node has no successor. - Swapping the order of assignments inside the loop โ you must assign
odd.next = even.nextand then advanceoddbefore assigningeven.next = odd.next; reversing this causeseven.nextto point to the same node as the oldeven, breaking the even chain. - Confusing node index parity with node value parity โ the problem groups nodes by their 1-based position (odd position vs. even position), not by whether the stored value is odd or even. A node with value 4 at position 1 belongs in the odd sublist.
- Returning
oddoreveninstead ofheadโ the first node never moves from its position in memory; only itsnextpointer changes. Always return the originalhead.
Related Problems
partition-listโ same two-sublist idea: split by a condition and reconnect the chainsreverse-linked-listโ foundational pointer manipulation underlying most linked list problemsswap-nodes-in-pairsโ re-links adjacent node pairs using a similar leap-frog pointer patternreorder-listโ also involves splitting a list in half and merging the halves back togetherremove-linked-list-elementsโ uses the same "skip a node by updating next" technique to bypass unwanted nodes