Reorder List
Problem
https://leetcode.com/problems/reorder-list/
You are given the head of a singly linked-list. The list can be represented as:
L0 → L1 → … → Ln - 1 → Ln
Reorder the list to be on the following form:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
You may not modify the values in the list’s nodes. Only nodes themselves may be changed.
Example 1:

Input: head = [1,2,3,4]
Output: [1,4,2,3]
Example 2:

Input: head = [1,2,3,4,5]
Output: [1,5,2,4,3]
Constraints:
The number of nodes in the list is in the range
[1, 5 * 10:sup:`4`].1 <= Node.val <= 1000
Pattern
Linked List, Two Pointers, Stack, Recursion
Approaches
Explanation
While reordering a linked list is hard, doing the same reordering for an
array is easy. First, we copy all the linked list nodes into an array.
Then we use two pointers i and j, starting from the beginning and
end of the array, and set nodes[i].next to nodes[j], and
nodes[j].next to nodes[i + 1]. We stop when the two pointers meet.
Finally, we set the next pointer of the last node to None.
Code
from __future__ import annotations
class ListNode:
"""Node in a linked list."""
def __init__(self, val: int = 0, next: ListNode | None = None):
self.val = val
self.next = next
@classmethod
def from_list(cls, vals: list[int]) -> ListNode | None:
dummy = cls(0)
node = dummy
for val in vals:
node.next = cls(val)
node = node.next
return dummy.next
def to_list(self) -> list:
result = []
node = self
while node:
result.append(node.val)
node = node.next
return result
def reorderList(head: ListNode | None) -> None:
"""Reorder ``head`` in-place by interleaving front and back."""
nodes = []
node = head
while node:
nodes.append(node)
node = node.next
i = 0
j = 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
Test
>>> from reorder_list__two_pointers import reorderList, ListNode
>>> head = ListNode.from_list([1, 2, 3, 4])
>>> reorderList(head)
>>> head.to_list()
[1, 4, 2, 3]
>>> head = ListNode.from_list([1, 2, 3, 4, 5])
>>> reorderList(head)
>>> head.to_list()
[1, 5, 2, 4, 3]
Complexity
\(n\) is the number of nodes
Measure |
Complexity |
Notes |
|---|---|---|
Time |
\(O(n)\) |
one pass through the original linked list, and one pass through the array |
Auxiliary Space |
\(O(n)\) |
array |
Explanation
Note the reordering keeps the first half of the list in the original order and reversed the second half of the list. Thus, we can reorder the linked list by splitting it into two halves, reversing the second half, and merging the two halves together.
Find the middle of the linked list using the slow and fast pointer technique. We start
fast = head.nextwhich means it is always at index \(2i + 1\) whenslowis at index \(i\). This means for even length lists,slowwill be the first middle node instead of the second middle node.Reverse the second half of the linked list. Starting from
slow, we reverse the linked list by settingslow.nexttoprev(initiallyNone), then movingprev = slowandslow = original_slow_next. This splits the linked list into two halves.1 -> 2 -> 3 -> 4 -> None
becomes
1 -> 2 -> None 4 -> 3 -> 2 -> None
Note that the first reversal
slow.next = Noneis what cuts the linked list into 2 halves.Merge the two halves together. Starting from the head of the first half (
head) and the head of the reversed second (whereprevends), we merge the two halves by settinghead.next = prevandprev.next = original_head_next, then movingheadandprevto their original next values.
The reason for fast = head.next becomes clear now. For an even length
list, the first half has one less node than the second half. Thus, we go
1 -> 4 -> 2 -> 3 -> None. Had we started fast = head, the split
would be
1 -> 2 -> 3 -> None
4 -> 3 -> None
which would result in 1 -> 4 -> 2 -> 3 -> 3 -> ..., which is
incorrect.
Code
from __future__ import annotations
class ListNode:
"""Node in a linked list."""
def __init__(self, val: int = 0, next: ListNode | None = None):
self.val = val
self.next = next
@classmethod
def from_list(cls, vals: list[int]) -> ListNode | None:
dummy = cls(0)
node = dummy
for val in vals:
node.next = cls(val)
node = node.next
return dummy.next
def to_list(self) -> list:
result = []
node = self
while node:
result.append(node.val)
node = node.next
return result
def reorderList(head: ListNode | None) -> None:
"""
Do not return anything, modify head in-place instead.
"""
slow = head
fast = head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# slow is at middle of list
prev = None
while slow:
original_next = slow.next
slow.next = prev
prev = slow
slow = original_next
# prev is at end of reversed second half
while head and prev:
original_head_next = head.next
original_prev_next = prev.next
head.next = prev
prev.next = original_head_next
head = original_head_next
prev = original_prev_next
Test
>>> from reorder_list__split_and_merge import reorderList, ListNode
>>> head = ListNode.from_list([1, 2, 3, 4])
>>> reorderList(head)
>>> head.to_list()
[1, 4, 2, 3]
>>> head = ListNode.from_list([1, 2, 3, 4, 5])
>>> reorderList(head)
>>> head.to_list()
[1, 5, 2, 4, 3]
Complexity
\(n\) is the number of nodes
Measure |
Complexity |
Notes |
|---|---|---|
Time |
\(O(n)\) |
one pass to find the middle, one pass to reverse, and one pass to merge |
Auxiliary Space |
\(O(1)\) |
modify list in place |