Merging and Partitioning
26 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)Sorted data arrives in pieces all the time: daily sales from two branches, two lists of ticket ids. This lesson shows how to join them in one pass and how to separate items by kind without a full sort.
Merge two sorted lists
Merge means combining two sorted lists into one sorted list by repeatedly taking the smaller front item. It takes O(n + m) time because each item is looked at once.
Two branches send daily sales, each list already sorted. Merge them in one pass: compare the front items, take the smaller one, and move that list's pointer forward.
a = [1, 4, 9]b = [2, 3, 10, 12]i = j = 0merged = []while i < len(a) and j < len(b): if a[i] <= b[j]: merged.append(a[i]) i += 1 else: merged.append(b[j]) j += 1merged += a[i:]merged += b[j:]print(merged)Example 1 · Merge with repeated values
def merge_sorted(a, b): i = j = 0 merged = [] while i < len(a) and j < len(b): if a[i] <= b[j]: merged.append(a[i]) i += 1 else: merged.append(b[j]) j += 1 return merged + a[i:] + b[j:]
print(merge_sorted([1, 2, 2, 5], [2, 3]))[1, 2, 2, 2, 3, 5]
What this code does
<=takes from the first list on a tie, so equal items keep their original order.- The leftovers are added with slices, which are empty when a list is used up.
- O(n + m) time and O(n + m) space for the result.
When one list runs out, the rest of the other list is already in order, so it is added as it is. Time is O(n + m).
Brute force and the built-ins
The shortest way is sorted(a + b). It ignores that the inputs are sorted and costs O(n log n). heapq.merge does the one-pass merge lazily. Interviews expect you to write the pointer version by hand.
from heapq import merge
a = [1, 4, 9]b = [2, 3, 10, 12]print(sorted(a + b))print(list(merge(a, b)))Example 2 · Merge many sorted lists with heapq
from heapq import merge
north = [3, 8, 20]south = [1, 9]east = [4, 5, 6]print(list(merge(north, south, east)))[1, 3, 4, 5, 6, 8, 9, 20]
What this code does
mergetakes any number of sorted inputs.- It yields items one at a time, so it works well with very large inputs.
- It does not sort: unsorted input gives a wrong result.
Both give the same answer. merge also accepts more than two sorted inputs.
Merge from the back, in place
A list nums1 has empty room at its end for the items of nums2. Fill it from the largest end, so no item you still need is overwritten.
nums1 = [2, 5, 8, 0, 0, 0]nums2 = [1, 6, 9]i, j, k = 2, 2, 5while j >= 0: if i >= 0 and nums1[i] > nums2[j]: nums1[k] = nums1[i] i -= 1 else: nums1[k] = nums2[j] j -= 1 k -= 1print(nums1)Example 3 · Merge into nums1 as a function
def merge_in_place(nums1, m, nums2, n): i, j, k = m - 1, n - 1, m + n - 1 while j >= 0: if i >= 0 and nums1[i] > nums2[j]: nums1[k] = nums1[i] i -= 1 else: nums1[k] = nums2[j] j -= 1 k -= 1
first = [4, 5, 6, 0, 0, 0]merge_in_place(first, 3, [1, 2, 3], 3)print(first)empty = [0]merge_in_place(empty, 0, [7], 1)print(empty)[1, 2, 3, 4, 5, 6] [7]
What this code does
mandnsay how many real items each list has.- The second call has nothing real in nums1, so everything comes from nums2.
- O(m + n) time and O(1) extra space.
k is the next free slot from the back. When nums2 is used up, the remaining front items of nums1 are already in place.
Partition: sort colours in one pass
Partition means rearranging a list so that items of one kind come before items of another kind. No full sort is needed, and one pass is often enough.
Orders carry a priority 0, 1 or 2. Sort them in one pass with three pointers: low is the next place for a 0, high the next place for a 2, and mid scans. This is the Dutch national flag problem.
colors = [2, 0, 2, 1, 1, 0]low, mid, high = 0, 0, len(colors) - 1while mid <= high: if colors[mid] == 0: colors[low], colors[mid] = colors[mid], colors[low] low += 1 mid += 1 elif colors[mid] == 1: mid += 1 else: colors[mid], colors[high] = colors[high], colors[mid] high -= 1print(colors)Example 4 · Sort colours by counting
from collections import Counter
colors = [2, 0, 2, 1, 1, 0]count = Counter(colors)colors[:] = [0] * count[0] + [1] * count[1] + [2] * count[2]print(colors)[0, 0, 1, 1, 2, 2]
What this code does
- A first pass counts each value and a second pass rewrites the list.
- Two passes, O(n) time and O(1) extra space, because there are only three values.
- The three-pointer version does the same in one pass.
Example 5 · Even numbers first with two pointers
orders = [3, 8, 5, 2, 7, 4]left, right = 0, len(orders) - 1while left < right: if orders[left] % 2 == 0: left += 1 elif orders[right] % 2 == 1: right -= 1 else: orders[left], orders[right] = orders[right], orders[left] left += 1 right -= 1print(orders)[4, 8, 2, 5, 7, 3]
What this code does
leftskips even items andrightskips odd items.- When
leftholds an odd item andrightan even one, they swap. - The order inside each group may change. That is fine for a partition.
After a swap with high, mid stays, because the item that arrived has not been checked yet. One pass, O(1) extra space.
Next step
Check your understanding
12 quick questions