# Merge Sort — Data Structures & Algorithms

Source: https://www.geekswithgeeks.com/en/dsa/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.

```python
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 out
```

## Trade-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.

**Quiz:** Merge sort's worst-case time is...

- [ ] O(n^2)
- [x] 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.
