Sorting Algorithms

Sorting arranges elements in order. It’s the most studied problem in CS because it’s foundational — many algorithms require sorted input, and sorting reveals deep ideas about complexity.

Why It Matters

Sorted data enables Binary Search (O(log n)), simplifies deduplication, and is required for merge operations. Knowing which sort to use and why is practical engineering knowledge.

Comparison

AlgorithmTime (avg)Time (worst)SpaceStableNotes
Bubble sortO(n^2)O(n^2)O(1)YesTeaching only
Insertion sortO(n^2)O(n^2)O(1)YesFast on small/nearly sorted
Merge sortO(n log n)O(n log n)O(n)YesGuaranteed performance
Quick sortO(n log n)O(n^2)O(log n)NoFastest in practice
Heap sortO(n log n)O(n log n)O(1)NoIn-place, not cache-friendly
Counting sortO(n + k)O(n + k)O(k)YesOnly for integers, k = range

Stable means equal elements keep their original relative order.

Merge Sort

Divide the array in half, recursively sort each half, then merge. Always O(n log n).

def merge_sort(arr: list) -> list:
    if len(arr) <= 1:
        return arr
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return _merge(left, right)
 
def _merge(a: list, b: list) -> list:
    result = []
    i = j = 0
    while i < len(a) and j < len(b):
        if a[i] <= b[j]:
            result.append(a[i]); i += 1
        else:
            result.append(b[j]); j += 1
    result.extend(a[i:])
    result.extend(b[j:])
    return result
 
assert merge_sort([38, 27, 43, 3, 9, 82, 10]) == [3, 9, 10, 27, 38, 43, 82]

Quick Sort

Pick a pivot, partition elements into less-than and greater-than, recurse. O(n log n) average, O(n^2) worst case (rare with random pivot).

import random
 
def quick_sort(arr: list) -> list:
    if len(arr) <= 1:
        return arr
    pivot = arr[random.randint(0, len(arr) - 1)]
    less = [x for x in arr if x < pivot]
    equal = [x for x in arr if x == pivot]
    greater = [x for x in arr if x > pivot]
    return quick_sort(less) + equal + quick_sort(greater)
 
assert quick_sort([38, 27, 43, 3, 9, 82, 10]) == [3, 9, 10, 27, 38, 43, 82]

When to Use What

  • Default: use your language’s built-in sort (Python: Timsort, O(n log n), stable)
  • Guaranteed O(n log n): merge sort
  • In-place needed: quick sort or heap sort
  • Small arrays / nearly sorted: insertion sort
  • Integer keys with small range: counting sort