Divide and conquer

Split, sort, merge — why merging two sorted lists is cheap.

Divide and conquer splits the input, solves the pieces, and combines the answers. Merging two already-sorted lists is linear. That is the engine under merge sort.

Goal

Merge two sorted lists, then sort by splitting until the pieces are tiny.

Merge two sorted lists

def merge(left, right):
    i = j = 0
    out = []
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            out.append(left[i])
            i += 1
        else:
            out.append(right[j])
            j += 1
    out.extend(left[i:])
    out.extend(right[j:])
    return out

print(merge([10.5, 22.0], [7.0, 31.0]))
print(merge([1, 3, 5], [2, 4]))

Each item is copied once. That is on the order of n, not .

Merge sort

def merge(left, right):
    i = j = 0
    out = []
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            out.append(left[i])
            i += 1
        else:
            out.append(right[j])
            j += 1
    out.extend(left[i:])
    out.extend(right[j:])
    return out

def merge_sort(xs):
    if len(xs) <= 1:
        return list(xs)
    mid = len(xs) // 2
    return merge(merge_sort(xs[:mid]), merge_sort(xs[mid:]))

print(merge_sort([31, 10.5, 22.0, 10.5, 7]))

Split until length 0 or 1 (already sorted), then merge on the way back. Depth is about log₂ n splits; each level copies n items.

Tip

This is why people say merge sort is “n log n”. You do not need the formula — print a tiny example and see the split.