Handbooks / Data Structures & Algorithms / Chapter 3

Sorting and Searching

7 pages · ~5 min✓ Reviewed

Builds on Linked Lists, Stacks and Queues. Next up: Trees and Graphs.

Data Structures & AlgorithmsPage 1

Big-O and Sorting Mental Model

1. Reading Big-O

  • Drop constants and lower terms: 3n² + 5n + 2 is O(n²)
  • An upper bound on growth as n grows
  • State which case it bounds: best, average or worst
  1. O(1)
  2. O(log n)
  3. O(n)
  4. O(n log n)
  5. O(n²)
  6. O(2ⁿ)
  7. O(n!)

2. Cases and Space

  • Space complexity = extra memory beyond the input
  • In-place sorts use O(1) or O(log n) stack
SortTimeExtra space
QuickO(n log n) avg, O(n²) worstO(log n) stack
MergeO(n log n) alwaysO(n)
InsertionO(n²) avg, O(n) bestO(1)

3. Sort Properties

  • Stable: equal keys keep their original order
  • In-place: no big auxiliary array
  • Comparison sorts cannot beat Ω(n log n)
Always name the case

Quick sort is O(n log n) average but O(n²) worst. Never quote one bound alone.

Data Structures & AlgorithmsPage 2

Simple Sorts: Bubble and Insertion

1. Bubble Sort

  • Swap adjacent out-of-order pairs
  • After pass k, the k largest are fixed at the end
  • No swap in a full pass → stop: O(n) on sorted input

2. Insertion Sort

  • A sorted prefix grows by one each step
  • Larger elements shift right; no swaps
  1. Take key
  2. Shift larger right
  3. Drop in gap
python
for i in range(1, len(a)):
    key, j = a[i], i - 1
    while j >= 0 and a[j] > key:
        a[j + 1] = a[j]
        j -= 1
    a[j + 1] = key

3. Which One

BubbleInsertion
BestO(n) early exitO(n) sorted
Avg / worstO(n²)O(n²)
SpaceO(1)O(1)
Stableyesyes
Moves~n²/2 swapsshifts only
  • Insertion wins on small or nearly sorted arrays
  • Timsort and introsort use it for tiny partitions
Pick

Both are O(n²), but insertion is almost always the better one.

Data Structures & AlgorithmsPage 3

Divide and Conquer Sorts: Merge and Quick

1. Merge Sort

  • Split in half, sort each, merge two sorted halves with two pointers
  • Always O(n log n), extra space O(n), stable
  • Ties: take from the left half first (<=)
  • < on ties breaks stability
python
if left[i] <= right[j]:
    out.append(left[i]); i += 1
else:
    out.append(right[j]); j += 1

<= keeps equal keys in order

2. Quick Sort

  • Pick a pivot, partition into smaller and larger, recurse
  • Average O(n log n), worst O(n²)
  • In place, O(log n) stack, not stable
  • Worst case: sorted input with first/last pivot
  • Fix: random or median-of-three pivot
Bound the stack

Recurse on the smaller side, loop on the larger: O(log n) stack worst case.

3. Which to Pick

MergeQuick
Worst caseO(n log n)O(n²)
Extra spaceO(n)O(log n)
Stableyesno
Pick forstability, linked lists, guaranteed boundsin-place speed, cache-friendly arrays
Data Structures & AlgorithmsPage 4

Heap Sort and Counting Sort

1. Heap Sort

  • Build max-heap in O(n), then swap root with last and sift down
  • O(n log n) always, O(1) space, not stable
  • 0-based: children of i are 2i+1, 2i+2; parent is (i-1)/2
  • Heapify from last non-leaf n/2-1 down to 0
  1. Build heapO(n)
  2. Swaproot with last
  3. Shrinkheap size - 1
  4. Sift downrepeat

2. Counting Sort

  1. Counteach key
  2. Prefix-sumend positions
  3. Placeright to left
  • O(n + k) time and space
  • Right-to-left placement makes it stable
  • Only for integer keys in a small range k
  • Base of radix sort

3. Compare

HeapCounting
TimeO(n log n)O(n + k)
SpaceO(1)O(n + k)
StableNoYes
Keyscomparablesmall ints
Beats n log n

Counting indexes by key value instead of comparing, so the comparison bound does not apply.

  • Interview: why does heapify start at n/2-1? Later nodes are leaves.
  • Interview: why is counting sort stable? Right-to-left keeps equal keys in order.
Data Structures & AlgorithmsPage 5

Binary Search and Its Variants

1. The Template

  • Needs a sorted array; each step halves the range
  • Time O(log n), space O(1) when iterative
  • Mid is lo+(hi-lo)/2, not (lo+hi)/2: the sum overflows fixed-width ints
python
def binary_search(a, t):
    lo, hi = 0, len(a) - 1
    while lo <= hi:
        mid = lo + (hi - lo) // 2
        if a[mid] == t: return mid
        if a[mid] < t: lo = mid + 1
        else: hi = mid - 1
    return -1

returns -1 if not found

2. Variants

VariantOn matchResult
First occurrencerecord mid, then hi = mid-1leftmost index
Last occurrencerecord mid, then lo = mid+1rightmost index
  • Search on answer: binary search any monotonic predicate (min capacity, square root)
  • Rotated array: one half is always sorted; check if the target lies in it, else go to the other half

3. Off-by-one Traps

  • lo<=hi with hi=mid: infinite loop
  • lo<hi with hi=mid-1: skips the answer
One template

Pick one template and stay consistent: lo<=hi with mid+1 and mid-1.

Data Structures & AlgorithmsPage 6

Sorting and Searching Cheat Sheet

1. Complexity and Stability

SortAvg timeSpaceStable (typical)
BubbleO(n²)O(1)yes
InsertionO(n²)O(1)yes
MergeO(n log n)O(n)yes
QuickO(n log n), worst O(n²)O(log n)no
HeapO(n log n)O(1)no
CountingO(n+k)O(n+k)yes
  • Best case O(n): bubble (with early exit) and insertion on sorted input
  • Merge and heap stay O(n log n) whatever the input

2. Choosing

NeedPick
Tiny or nearly sortedinsertion
Stable and guaranteedmerge
Fastest general in-memoryquick
O(1) space with guaranteeheap
Small integer keyscounting

3. Binary Search

  • O(log n), sorted input, loop while lo <= hi
  • mid = lo + (hi - lo) / 2, never (lo + hi) / 2
  • Lower bound = first index >= target (first occurrence)
  • Upper bound = first index > target
Last occurrence

It is upper bound - 1, not the upper bound itself.

Data Structures & AlgorithmsPage 7

Check yourself

A merge step takes from the right half when left < right is false. Sorting records by key, what breaks?
  • On ties the right element goes first, so equal keys swap order.
  • Merge sort turns unstable; use <= to take left on ties.
First-occurrence search for 2 in [1,2,2,2,3] records mid on a match and sets hi=mid-1. What index returns?
  • mid=2 matches, hi=1; mid=0 is too small, lo=1; mid=1 matches, hi=0.
  • Returns 1, the leftmost 2.
Counting sort fills the output scanning the input left to right with decrementing prefix counts. What goes wrong?
  • Prefix sums give the last slot of each key, so the first equal element lands last.
  • Equal keys come out reversed: not stable. Scan right to left.