Coding Interview Patterns Module 10 – Linked list patterns
Merging, splitting and partitioning lists
Partition a linked list around a value while keeping the order, merge two sorted lists, split a list into k parts, and sort it with merge sort.
What you will learn
- Partition a list around a value in one pass with two dummy tails, keeping the order inside each group
- Merge two sorted lists with a dummy head so that equal values keep their order
- Split a list into k parts whose sizes differ by at most one
- Sort a linked list with merge sort in O(n log n) time, and explain the first-middle split
- Compare the recursive merge sort with a bottom-up one that needs no recursion
Before you start
What you’ll learn · Before you start
What you will learn
- Partition a list around a value in one pass with two dummy tails, keeping the order inside each group
- Merge two sorted lists with a dummy head so that equal values keep their order
- Split a list into k parts whose sizes differ by at most one
- Sort a linked list with merge sort in O(n log n) time, and explain the first-middle split
- Compare the recursive merge sort with a bottom-up one that needs no recursion
Before you start
On this page
Merging, splitting and partitioning lists all build new chains out of nodes that already exist. Partitioning lists around a value moves every node below the value in front of the others and keeps both groups in their order. One pass does it: keep two chains, each behind a dummy node with a tail pointer, append every node to the chain it belongs to, and join the two chains at the end, with O(1) extra memory. Merging two sorted lists appends the smaller front node each time. Splitting a list into k parts cuts it after n // k nodes, one more for the first n mod k parts. Merge sort on a list combines the last two ideas and sorts in O(n log n) time.
The problem: express orders first
The problem
A grocery delivery service keeps the day’s orders as a singly linked list of delivery times, in minutes. Before
dispatch, every order due in less than limit minutes must come before every other order, and inside each group
the orders keep their order. Rearrange the nodes you are given, creating none except dummies, with O(1) extra
memory, and return the new head. A list has up to 10⁵ orders.
With limit = 40, 45 → 20 → 90 → 15 → 60 → 30 becomes 20 → 15 → 30 → 45 → 90 → 60. An order due in exactly 40
minutes is not express, so 40 → 25 becomes 25 → 40, and a list with no express order, such as 50 → 70, stays
as it is.
The brute force: swap neighbours until nothing moves
The idea of bubble sort respects every rule: whenever a standard order is directly followed by an express one, swap the two nodes, and repeat whole passes until none swaps. Swapping only neighbours that are out of place keeps each group’s order, and nothing is stored, but an express order at the end must step past every standard order before it.
brute/brute_force.py
"""The brute force for putting express orders first: swap neighbouring nodes, as bubble sort would.
Whenever a standard order is followed directly by an express order, swap the two nodes (not their values), and
repeat until a pass makes no swap. Swapping only neighbours that are out of place keeps each group's order, and it
needs O(1) memory, but an express order at the end must travel past every standard order before it.
"""
from lists import ListNode, from_list, show
def express_first_by_swaps(head, limit):
"""Return (new head, pairs of neighbours looked at)."""
dummy = ListNode(None, head)
looked, changed = 0, True
while changed:
changed = False
prev = dummy
while prev.next is not None and prev.next.next is not None:
a, b = prev.next, prev.next.next
looked += 1
if a.val >= limit and b.val < limit: # standard, then express: swap the two nodes
a.next, b.next, prev.next = b.next, a, b
changed = True
prev = prev.next
return dummy.next, looked
for times in ([45, 20, 90, 15, 60, 30], [50, 70], [40, 25]):
head, looked = express_first_by_swaps(from_list(times), 40)
print(f"{times} with limit 40: {show(head)}")
for n in (500, 1_000, 2_000): # the worst case: every standard order first, then every express order
kept, looked = express_first_by_swaps(from_list([90] * (n // 2) + [10] * (n // 2)), 40)
print(f"n = {n:>5,}, express orders last: {looked:>9,} pairs looked at")
n = 100_000
work = (n // 2 + 1) * (n - 1)
print(f"n = {n:,}: about {work:,} pairs, {work / 1e7 / 60:.0f} minutes at 10^7 a second") Output
[45, 20, 90, 15, 60, 30] with limit 40: 20 -> 15 -> 30 -> 45 -> 90 -> 60 -> None [50, 70] with limit 40: 50 -> 70 -> None [40, 25] with limit 40: 25 -> 40 -> None n = 500, express orders last: 125,249 pairs looked at n = 1,000, express orders last: 500,499 pairs looked at n = 2,000, express orders last: 2,000,999 pairs looked at n = 100,000: about 5,000,049,999 pairs, 8 minutes at 10^7 a second
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 brute_force.py
brute/lists.py
"""Test helpers for singly linked lists: build one from a Python list, read it back, and print it."""
class ListNode:
"""One node: a value and a reference to the next node (None after the last node)."""
def __init__(self, val, next=None):
self.val = val
self.next = next
def from_list(values):
"""Build a linked list holding `values` in order and return its head (None for no values)."""
dummy = ListNode(None)
tail = dummy
for v in values:
tail.next = ListNode(v)
tail = tail.next
return dummy.next
def to_list(head):
"""The values from `head` to the end; raises ValueError if the list loops back on itself."""
values, seen = [], set()
node = head
while node is not None:
if id(node) in seen:
raise ValueError(f"cycle: after {len(values)} nodes the list returns to a node already seen")
seen.add(id(node))
values.append(node.val)
node = node.next
return values
def show(head):
"""The list as one line of text, such as 4 -> 9 -> 15 -> None."""
return " -> ".join([*map(str, to_list(head)), "None"]) Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
When every express order comes last, each pass moves them one place, so n/2 + 1 passes look at n − 1 pairs each: 5 × 10⁹ pairs for 10⁵ orders, about 8 minutes of CPython. Copying the values into an array and writing them back is fast, but it costs O(n) memory and moves orders between nodes that other code may still hold.
What to notice
The answer is two lists glued together: the express orders in their original order, then the standard ones in theirs. Walking once and appending each node to the end of its own list keeps both orders automatically, and a dummy in front of each list removes the special case of the first append, as in the toolkit lesson.
Two chains behind two dummy nodes, joined at the end
Text description of the diagram
The input list is 45, 20, 90, 15, 60, 30, with a limit of 40 minutes. The orders 20, 15 and 30 are below the limit and drawn in green; 45, 90 and 60 are not.
Below it are two chains, each starting at a dummy node drawn with a dashed border. The express chain holds 20, 15 and 30 in the order they appeared, and its tail pointer e is on 30. The standard chain holds 45, 90 and 60 in their order, and its tail pointer s is on 60, whose next is set to None.
An arrow from 30 to 45 shows the join, e.next = standard.next, which links the end of the express chain to the first real node of the standard chain. The result is 20, 15, 30, 45, 90, 60: every express order first, and each group in its original order.
Two dummy tails, step by step Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- The invariant, and the proof that the order is kept
- A dry run on the example
- Merging two sorted lists, with ties kept in order
- Splitting into k parts: n // k nodes, one more for the first n mod k
- Merge sort on a list in O(n log n)
- The code in five languages
- Edge cases and slips, caught by a stress test
- Variants, with answers
- Exercise: sort a list of orders by relinking its nodes
- Sort a list of orders by relinking its nodes
- A practice set with answers
- Follow-up questions, answered
- Check yourself
- Interview questions
- Why do two dummy nodes make a stable partition of a linked list simple?
- Your stable partition of a linked list around a value passes the samples, but one hidden test times out and printing its result never ends. You build two chains behind dummies and join them. What is the most likely bug, which inputs trigger it, and how do you test for it?
- Sort a singly linked list of up to 10⁶ nodes in O(n log n) time with O(1) extra memory: no recursion and no array. Describe the algorithm and its pointer bookkeeping, and say how you would test it.
- A dispatcher splits a linked list of n orders into k vans, in order, so that loads differ by at most one and an earlier van is never lighter. n and k can reach 10⁶, and k can exceed n. Give the loads for n = 10 and k = 4, the algorithm, and what changes if the length is not known.
- Key takeaways
5 runnable examples (C++, Go, Java, JavaScript, Python, lists.mjs, lists.py, measure.py) · 1 exercise with sample tests · 3 interview answers · about 23 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
References
- Sorting Techniques: sort stability and complex sorts (Python documentation) (Python Software Foundation)
- tracemalloc: trace memory allocations (Python Software Foundation)
- Array.prototype.sort() (stability) (MDN Web Docs)
- Collections.sort (Java SE 25): a stable sort (Oracle)
- std::list::sort (cppreference.com) (cppreference.com)
- std::list::merge (cppreference.com) (cppreference.com)
- Package slices: SortStableFunc (The Go Authors)
- AddressSanitizer (The LLVM Project)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress