Lesson 26 / 42
Merge Sort
Divide the array in half, sort each, then merge — O(n log n) guaranteed.
Divide, sort, merge
Split until each piece has one element (trivially sorted). Then repeatedly merge two sorted lists by comparing their fronts. log n levels of splitting × O(n) work to merge each level = O(n log n), always.
Implementation
The merge step is the heart: two indices walking two sorted halves.
def merge_sort(a):
if len(a) <= 1:
return a
mid = len(a) // 2
left = merge_sort(a[:mid])
right = merge_sort(a[mid:])
return merge(left, right)
def merge(l, r):
out, i, j = [], 0, 0
while i < len(l) and j < len(r):
if l[i] <= r[j]:
out.append(l[i]); i += 1
else:
out.append(r[j]); j += 1
out.extend(l[i:]); out.extend(r[j:])
return outTrade-offs
Pros: predictable O(n log n), stable, great for linked lists and external (on-disk) sorting. Con: needs O(n) extra space for the merge buffer.
Quick check: Merge sort's worst-case time is...
- O(n^2)
- O(n log n)
- O(n)
Answer
O(n log n) — The split is always balanced, so it's O(n log n) in every case — unlike quicksort.