Section 5 · 8 min read
Searching and sorting
Linear and binary search, the comparison sorts and linear-time sorts, their costs, stability and in-place behaviour, and the comparison lower bound.
On this page
Searching
Linear search
Scan elements one by one. Worst case comparisons, time, works on unsorted data and linked lists. If the key is equally likely to be at any of the positions, a successful search makes comparisons on average.
Binary search
Works on a sorted array with random access. Each step compares the key with the middle element and discards half the range.
binary_search(A, n, key): // A[0..n-1] sorted
lo = 0; hi = n - 1
while lo <= hi:
mid = (lo + hi) / 2 // integer division
if A[mid] == key: return mid
if key < A[mid]: hi = mid - 1
else: lo = mid + 1
return -1
With probes the algorithm can handle an array of at most elements, so the worst case is probes. Time , iterative space . On a linked list binary search loses its advantage because reaching the middle costs .
Common variants: first occurrence (on a match, record it and continue left), lower bound (first index with key), and search in a rotated sorted array (one half is always sorted). Interpolation search averages on uniformly distributed keys but is in the worst case.
Comparison sorts
Selection sort
Repeatedly select the minimum of the unsorted suffix and swap it to the front. Always comparisons and at most swaps, so it is the right choice when writes are expensive. Not stable in the usual array form.
Bubble sort
Repeatedly swap adjacent out-of-order pairs. After pass the largest elements are in place. With an early-exit flag, a sorted input finishes in one pass of comparisons. The number of swaps equals the number of inversions.
Insertion sort
Insert into the sorted prefix by shifting larger elements right.
for i = 1 to n - 1:
key = A[i]; j = i - 1
while j >= 0 and A[j] > key:
A[j + 1] = A[j]; j = j - 1
A[j + 1] = key
Shifts equal the number of inversions . Comparisons lie between and . Best case (sorted) , worst case (reverse sorted) . It is stable, in place, online, and fast on nearly sorted data. Using binary search to find the position cuts comparisons to but shifts remain in the worst case.
Merge sort
Split in half, sort each half, merge. in every case. Merging lists of sizes and takes between and comparisons. The worst-case total for is . Needs extra space for arrays, but sorts linked lists with extra space. Stable.
Quicksort
Partition around a pivot, then recurse on both sides.
partition(A, lo, hi): // Lomuto, pivot = A[hi]
p = A[hi]; i = lo - 1
for j = lo to hi - 1:
if A[j] <= p:
i = i + 1; swap A[i], A[j]
swap A[i + 1], A[hi]
return i + 1
Partition costs . Balanced splits give ; any constant-ratio split such as 1:9 still gives . The worst case occurs when the pivot is always the minimum or maximum, which happens for first- or last-element pivots on sorted or reverse-sorted input, and on arrays of all-equal keys with Lomuto. Randomised pivot gives expected , about comparisons. Not stable. In place apart from the recursion stack.
Heapsort
Build a max-heap in , then extract the maximum times at each. in all cases, in place, not stable.
Comparison table
| Algorithm | Best | Average | Worst | Stable | In place |
|---|---|---|---|---|---|
| Selection | No | Yes | |||
| Bubble (with flag) | Yes | Yes | |||
| Insertion | Yes | Yes | |||
| Merge | Yes | No () | |||
| Quick | No | Yes (stack only) | |||
| Heap | No | Yes | |||
| Counting | Yes | No | |||
| Radix ( digits) | Yes | No |
Lower bound
Any comparison sort corresponds to a binary decision tree with at least leaves, so its height is at least . Merge sort and heapsort are therefore asymptotically optimal. The minimum number of comparisons in the worst case for 5 elements is as a lower bound (and 7 is achievable).
Linear-time sorts
These avoid the lower bound by not using comparisons.
- Counting sort: keys in . Count occurrences, take prefix sums, then place elements scanning the input right to left so the sort is stable. time and space.
- Radix sort: sort by the least significant digit first, using a stable sort for each digit. With digits in base it costs . Keys up to written in base have digits, so radix sort is for constant . Using an unstable per-digit sort breaks correctness.
- Bucket sort: for keys uniform in , expected ; worst case is the cost of the bucket sort used inside one bucket.
Selection
The -th smallest element can be found by quickselect in expected , worst case . The median-of-medians pivot (groups of 5) guarantees worst case. Finding both minimum and maximum needs only comparisons by processing elements in pairs, against for two separate scans. The second largest needs comparisons using a tournament.
Worked examples
Worked example 1: Binary search probes
What is the maximum number of probes for a successful or unsuccessful binary search in a sorted array of 100 elements?
We need the smallest with . is too small and suffices, so the answer is 7. Check with the other form: .
Worked example 2: Lomuto partition trace
Partition with pivot 6 (last element), indices from 0.
- : 3 6, , swap with itself.
- : 8 skipped.
- : 2, , swap A[1], A[2]: .
- : 5, : .
- : 1, : .
- : 4, : .
- : 7 skipped.
Final swap of A[5] and A[7] gives . The pivot lands at index 5, exactly the number of elements not larger than it.
Worked example 3: Insertion sort cost
Sort with insertion sort. Count shifts and comparisons.
Inversions: (2,1), (5,1), (5,4), (5,3), (4,3), so 5 shifts. Comparisons per step:
- Inserting 5 into : 1 comparison (stops at 2).
- Inserting 1 into : 2 comparisons (passes 5 and 2, then falls below 0, so no stopping comparison).
- Inserting 4 into : 2 comparisons (passes 5, stops at 2).
- Inserting 3 into : 3 comparisons (passes 5 and 4, stops at 2). Total comparisons and 5 shifts.
Worked example 4: Radix sort cost
Sort integers in the range . Comparison sorts need . In base each key has 2 digits, so two counting-sort passes of each give overall. If the range were , each key would have about base- digits and radix sort would cost , worse than merge sort.
Common traps
- Stability matters only when equal keys exist; the classic unstable sorts are selection, quick and heap.
- Quicksort worst case also appears on all-equal keys with Lomuto partition, not only on sorted input.
- "In place" usually allows stack; merge sort on arrays is still not in place.
- Insertion sort is where is the inversion count; a nearly sorted array is linear.
- Binary search needs random access. Questions about binary search on a linked list expect .
- Heap building is , not ; the sort phase is what costs .
- Selection sort does comparisons even on sorted input.
Quick revision
- Binary search worst case: probes.
- Comparison sort lower bound: .
- Stable: insertion, bubble, merge, counting, radix. Unstable: selection, quick, heap.
- Fewest swaps: selection sort ().
- Merge two sorted lists of sizes : at most comparisons.
- Min and max together: comparisons.
- Median of medians: linear worst case; quickselect: linear expected.
- Radix sort needs a stable digit sort.