DSA sheet · Linked List · Basic operations pattern
Odd Even Linked List
The last question of the basic-operations pattern. We rearrange a list so that all nodes at odd positions come first and all nodes at even positions come after them. The teacher starts with an array-based brute force, then builds the optimal answer: split the list into two chains in place with two pointers, then join the chains. It is a great exercise in rewiring .next without losing any node, and in finding the exact loop condition for odd and even lengths.
Every part below follows the same order:
① the question in simple words → ② what the constraints tell us → ③ intuition → ④ building the logic from examples → ⑤ approach steps → ⑥ code → ⑦ code line by line → ⑧ dry run → ⑨ complexity & remember
- Part 0 · Linked list basics you need for this page
- Part A · Brute force with an array
- Part B · Optimal: two chains in place
- Part C · Revision page
Part 0 · Before starting
What is a linked list node?
A linked list is a chain of nodes. Each node holds a value (val) and a link to the next node (next). The last node's next is None.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val # the number stored in this node
self.next = next # the next node, or None at the end[1] → [2] → [3] → [4] → [5] → None ↑ head
- head points to the first node. Never move it while working: if you lose it, you lose the whole list. Use helper pointers (aliases) instead. Here our aliases will be called
oddandeven. - Walking:
curr = curr.nextmoves a pointer one node forward, until it becomesNone. - No index access: a linked list stores no positions. To reach position i you walk i links, so it costs O(i). "Odd and even positions" are just a way of describing the nodes; the list itself doesn't know them.
- Save next before changing a pointer. If you overwrite
a.next, the node thataused to point to can be lost forever, unless another pointer still holds it. This rule decides the whole design of Part B.
Positions, not values
Position 1 is the head, position 2 is the next node, and so on. "Odd nodes" means the nodes at positions 1, 3, 5, …; "even nodes" means positions 2, 4, 6, …. The values inside don't matter at all.
Part A · Brute force with an array
LeetCode 328 · Odd Even Linked List
1The question in simple words
Given the head of a list, rearrange it so that the nodes at odd positions come first (keeping their order), followed by the nodes at even positions (keeping their order). Return the head of the result.
input: [1] → [2] → [3] → [4] → [5] → None
output: [1] → [3] → [5] → [2] → [4] → None
└── odd ───────┘ └ even ┘input: [0] → [3] → [5] → [1] → [4] → None output: [0] → [5] → [4] → [3] → [1] → None
In example 1, 1 links to 3, 3 links to 5, and after 5 (the end of the odd group) comes the first even node, 2, then 4. In example 2, the teacher swaps in different values to show that we pick nodes by position: positions 1, 3, 5 hold 0, 5, 4 and positions 2, 4 hold 3, 1.
2What the constraints tell us
- Number of nodes: 0 to 10⁴. 0 is allowed, so the head can be
None→ we need a base case. - An O(n²) solution would be (10⁴)² = 10⁸ operations, right at the edge where we expect TLE. So we aim for O(n).
- LeetCode also asks for O(1) extra space. The brute force below breaks that rule, which is why it's only a first step.
3Intuition: copy out, sort into two piles, rebuild
Arrays have indices; linked lists don't. So the simplest first idea: copy every value into an array, where positions are easy to see. Then deal the values into two piles (odd positions, even positions), build a new list for each pile, and join the end of the first pile to the start of the second.
4Building the logic from the example
array: index 0 1 2 3 4
value 1 2 3 4 5
position 1 2 3 4 5
- Walk the list once and append every value to the array.
- Go over the array with an index
i. Ifi % 2 == 0(indices 0, 2, 4: values 1, 3, 5), the node goes to the first pile. Otherwise (indices 1, 3: values 2, 4), it goes to the second pile. - When everything is placed, connect the last node of the first pile to the head of the second pile (2). So we must remember where the second pile starts.
→ Both, depending on how you count. Arrays count from 0, list positions count from 1, so they are always off by one. Array index 0, 2, 4 (even indices) are list positions 1, 3, 5 (odd positions). Just remember: even array index = odd list position. The teacher points out exactly this mix-up.
5Approach steps
- If the list is empty, return it.
- Walk the list and copy all values into an array.
- For each index i: create a new node; put it on the "odd" chain if i is even, otherwise on the "even" chain. Keep a head and a tail for each chain.
- Link the odd chain's tail to the even chain's head.
- Return the odd chain's head.
6Code (Python)
class Solution:
def oddEvenList(self, head):
if head is None:
return head
vals = [] # 1) copy values into an array
curr = head
while curr:
vals.append(curr.val)
curr = curr.next
odd_head = odd_tail = None # 2) build two new chains
even_head = even_tail = None
for i, v in enumerate(vals):
node = ListNode(v)
if i % 2 == 0: # index 0, 2, 4 = positions 1, 3, 5
if odd_head is None:
odd_head = odd_tail = node
else:
odd_tail.next = node
odd_tail = node
else: # index 1, 3 = positions 2, 4
if even_head is None:
even_head = even_tail = node
else:
even_tail.next = node
even_tail = node
odd_tail.next = even_head # 3) odd chain, then even chain
return odd_head7Code line by line
| line | what it means |
|---|---|
| if head is None: return head | 0 nodes is allowed: nothing to rearrange. |
| while curr: vals.append(curr.val) | Copy every value into a Python list, so we get indices. |
| odd_head = odd_tail = None | Each new chain needs a head (to remember where it starts) and a tail (to add the next node in O(1)). |
| if i % 2 == 0: | Even array index = odd list position. |
| if odd_head is None: odd_head = odd_tail = node | The first node of a chain is both its head and its tail. |
| odd_tail.next = node odd_tail = node | Hang the new node after the tail, then the new node becomes the tail. |
| odd_tail.next = even_head | Join: the end of the odd chain points to the start of the even chain. (If there's only one node, even_head is None, which is exactly right.) |
8Dry run on 1 → 2 → 3 → 4 → 5
| i | value | pile | odd chain | even chain |
|---|---|---|---|---|
| 0 | 1 | odd | 1 | — |
| 1 | 2 | even | 1 | 2 |
| 2 | 3 | odd | 1 → 3 | 2 |
| 3 | 4 | even | 1 → 3 | 2 → 4 |
| 4 | 5 | odd | 1 → 3 → 5 | 2 → 4 |
| join | 1 → 3 → 5 → 2 → 4 → None | |||
9Complexity & remember
- Time O(n): one pass to copy, one pass to build.
- Space O(2n), which is O(n): the array holds n values, and the two new chains hold about n/2 + n/2 = n new nodes. The teacher counts these separately to show how wasteful it is, when the nodes we need are already sitting in the original list.
Part B · Optimal: two chains in place
1The question (same as Part A)
Same rearrangement, but now with O(1) extra space: no array, no new nodes. We only change .next links of the nodes we already have.
2What the constraints tell us
- 0 nodes is possible, and 1 node too. With 0 or 1 node there is nothing to rearrange, so return head straight away. This also makes sure
head.nextis safe to read afterwards. - With 2 nodes the list is already "odd then even" (1 → 2). The code handles it without a special case: the loop simply doesn't run.
- O(n) time is the target, and one pass gives it.
3Intuition: unzip the list into two chains
Think of the list as a zipper with teeth from two sides: odd, even, odd, even… We pull it apart into two chains, odd nodes linked to each other and even nodes linked to each other, and finally hang the even chain after the odd chain.
- Pointer
oddwalks along the odd nodes; pointerevenwalks along the even nodes. - Each odd node should skip over the even node in front of it, and link to the next odd node. Each even node does the same.
- We keep one extra pointer,
even_head, fixed on the first even node (2), because at the end the last odd node must link to it.
4Building the logic from the example
First try: odd.next = odd.next.next, and why we need the even pointer
We want 1 to link to 3. 3 is odd.next.next, so the first idea is odd.next = odd.next.next. That does link 1 to 3, but now nobody points to 2. Node 2 is lost forever, and we needed it for the even chain. So before rewiring, we keep a pointer on 2: even = head.next. That's the "save next before changing a pointer" rule in action.
Setup: odd = head (node 1), even = head.next (node 2), even_head = even (stays on node 2 forever).
The four moves, found one at a time
[1] → [2] → [3] → [4] → [5] → None ↑ ↑ odd even, even_head
- Link odd to the next odd node. The next odd node (3) is right after
even, so:odd.next = even.next. Now 1 → 3. Node 2 isn't lost, becauseevenstill holds it, and 2 still points to 3. - Move odd forward. We're done with node 1, there's no reason to wait there.
odd = odd.next. Because we already changed 1's next to 3, this movesoddto 3, not to 2. - Link even to the next even node. The next even node (4) is right after the new
odd:even.next = odd.next. Now 2 → 4. - Move even forward.
even = even.next, soevenis on 4.
after one round (odd is on [3], even is on [4]):
links now: 1 → 3, 2 → 4, 3 → 4 (not changed yet), 4 → 5
odd chain: [1] → [3]
even chain: [2] → [4] → [5] → None
↑ ↑
even_head even
Repeat: 3 should link to 5, which is even.next. That is the same line as before, odd.next = even.next! The four moves repeat exactly, so they go in a loop.
When do we stop? Two cases
Odd length (1 2 3 4 5): second round: 3 → 5, odd = 5, 4 → 5.next = None, even = None. There are no nodes left to place. even became None, that's the stop signal.
Even length (1 2 3 4): after the first round, odd is on 3 and even is on 4. 1 → 3 and 2 → 4 are done. Does 3 need to link to another odd node? No, there isn't one. Does 4? No. So we stop, but even isn't None, it's on node 4. The signal here is even.next is None.
So: stop if even is None or even.next is None. Turned around for the while: keep going while even is not None and even.next is not None.
and, and why check even first?→ We need both to be true to continue. And the order matters: if
even is None, Python's and stops right there and never evaluates even.next. If we wrote even.next first, or used or (which goes on to check the second part when the first is false), we'd read "next of None" → crash. The teacher first said "or" while speaking and then corrected it to "and" for this reason.even and not odd?→ The even pointer is always one step ahead of the odd pointer in the original order, so it reaches the end first. Each round needs
even.next (the next odd node) to exist; if it does, odd.next after moving is safe to read as well.After the loop: join the two chains
Can we return head right after the loop? Check with 1 2 3 4. After the loop the links are 1 → 3, 2 → 4, and 3 → 4 (never changed). Walking from head gives 1 → 3 → 4, so node 2 is missing. We must link the last odd node to the first even node: odd.next = even_head. That's why we saved even_head at the start: even itself has moved away from node 2.
Result: 1 → 3 → 2 → 4 → None. ✓ And the even chain already ends in None (the last even node's next was set to odd.next, or it was the real tail), so nothing loops.
5Approach steps
- If head is None or head.next is None → return head.
odd = head,even = head.next,even_head = even.- While
evenandeven.nextexist:odd.next = even.next;odd = odd.next;even.next = odd.next;even = even.next. - Join:
odd.next = even_head. - Return
head(node 1 is still first).
6Code (Python)
class Solution:
def oddEvenList(self, head):
if head is None or head.next is None: # 0 or 1 node
return head
odd = head
even = head.next
even_head = even # remember where the even chain starts
while even is not None and even.next is not None:
odd.next = even.next # odd skips over even
odd = odd.next
even.next = odd.next # even skips over the new odd
even = even.next
odd.next = even_head # odd chain, then even chain
return head7Code line by line
| line | what it means |
|---|---|
| if head is None or head.next is None: return head | 0 or 1 node: already "rearranged". Also protects head.next below. |
| odd = head even = head.next | Two walkers: one on the first odd node, one on the first even node. even also saves node 2 so it isn't lost when 1 skips over it. |
| even_head = even | A pointer that never moves: we need the first even node for the final join. |
| while even is not None and even.next is not None: | Stop when even is None (odd length) or is the last node (even length). and + this order avoids reading None.next. |
| odd.next = even.next | Rewire: the current odd node now points to the next odd node. |
| odd = odd.next | Move to that next odd node. |
| even.next = odd.next | Rewire: the current even node points to the next even node (or None). |
| even = even.next | Move to that next even node (or None). |
| odd.next = even_head | Hang the even chain after the last odd node. |
| return head | The head (node 1) never changed position. |
8Dry run
Odd length: 1 → 2 → 3 → 4 → 5 (even_head = [2] the whole time)
| step | pointers | what changes | chains after this step |
|---|---|---|---|
| 0 | odd=[1], even=[2] | setup | 1→2→3→4→5 |
| 1 | check: even=[2], even.next=[3] ✓ | 1.next = [3]; odd=[3] | odd: 1→3 even: 2→3 |
| 2 | 2.next = [4]; even=[4] | odd: 1→3 even: 2→4 | |
| 3 | check: even=[4], even.next=[5] ✓ | 3.next = [5]; odd=[5] | odd: 1→3→5 even: 2→4→5 |
| 4 | 4.next = None; even=None | odd: 1→3→5 even: 2→4 | |
| 5 | check: even is None → stop | 5.next = even_head [2] | 1→3→5→2→4→None ✓ |
Snapshots:
start: [1] → [2] → [3] → [4] → [5] → None
odd even
after round 1: [1] → [3] → [4] → [5] → None odd = [3]
[2] → [4] (2 skips 3) even = [4]
after round 2: [1] → [3] → [5] → None odd = [5]
[2] → [4] → None even = None
after the join: [1] → [3] → [5] → [2] → [4] → None
Even length: 1 → 2 → 3 → 4
| step | pointers | what changes | chains after this step |
|---|---|---|---|
| 0 | odd=[1], even=[2] | setup | 1→2→3→4 |
| 1 | even=[2], even.next=[3] ✓ | 1.next=[3], odd=[3], 2.next=[4], even=[4] | odd: 1→3→4 even: 2→4 |
| 2 | even=[4], even.next=None → stop | 3.next = even_head [2] | 1→3→2→4→None ✓ |
Without the join, walking from head would give 1 → 3 → 4 (node 2 missing), which is why even_head exists.
9Complexity & remember
- Time O(n): the pointers walk the list once; every round places two nodes.
- Space O(1): just three pointers (
odd,even,even_head). No array, no new nodes. The teacher's submission beat 100% for this reason.
odd=head, even=head.next, even_head=even · loop while even and even.next: odd.next=even.next, odd=odd.next, even.next=odd.next, even=even.next · then odd.next = even_head.Part C · Revision page
| Brute force (array) | Optimal (two chains) | |
|---|---|---|
| idea | copy values out, rebuild two new lists, join | rewire the existing nodes into two chains, join |
| new nodes? | yes, n of them | no |
| time | O(n) | O(n) |
| extra space | O(2n): array + new nodes | O(1) |
| key detail | even array index = odd position | save even_head; loop while even and even.next |
| length | what stops the loop | where odd ends |
|---|---|---|
| odd (1 2 3 4 5) | even becomes None | last node (5) |
| even (1 2 3 4) | even.next is None | second-last node (3) |
2. Two walkers:
odd on 1, even on 2, plus even_head that never moves.3. Each round: odd skips even, move odd; even skips odd, move even.
4.
while even and even.next: covers odd and even lengths, and avoids None.next.5. Finish with
odd.next = even_head, return head. O(n) time, O(1) space.✗
odd.next = odd.next.next without a pointer on the even node (it's lost)✗ forgetting
even_head, or using even for the join (it has moved)✗
or instead of and in the loop, or checking even.next before even✗ missing the 0 / 1 node base case (
head.next on None)class ListNode:
def __init__(self, val=0, next=None):
self.val, self.next = val, next
def build(vals):
head = None
for v in reversed(vals):
head = ListNode(v, head)
return head
def to_list(head):
out = []
while head:
out.append(head.val)
head = head.next
return out
s = Solution()
print(to_list(s.oddEvenList(build([1, 2, 3, 4, 5])))) # [1, 3, 5, 2, 4]
print(to_list(s.oddEvenList(build([1, 2, 3, 4])))) # [1, 3, 2, 4]
print(to_list(s.oddEvenList(build([0, 3, 5, 1, 4])))) # [0, 5, 4, 3, 1]
print(to_list(s.oddEvenList(build([])))) # []Based on this video: Odd Even Linked List