7 routes42 sections324 units

Unit 2.3 · about 5 h

98 / 324

Binary search: intervals, bounds and feasibility predicates

Binary search fails in production through boundary bugs, not through asymptotics. One invariant derives every variant, and the same invariant searches answer spaces that are not arrays at all.

  • derive
  • implement

You can now

  • Prove the binary-search loop invariant and derive safe boundary updates for lower-bound, upper-bound and first-true variants.
  • Implement exact-match, lower-bound, upper-bound and answer-space search from the invariant, with differential tests against bisect.
  • Turn a monotone feasibility predicate into lo/hi bounds with cost \(O(F \log W)\), and defend why monotonicity (not sortedness) is the requirement.

Covers

  • Binary-search interval conventions and termination
  • Lower bound, upper bound and duplicate handling
  • Binary search on a monotone feasibility predicate

Study, in this order

4 steps

  1. Binary search: the canonical version
    DSA Handbook - search, trees and heaps

    Pick the template from the question, not the input; Template A: exact-match on a closed interval; Template B: lower-bound on a half-open interval; Template C: predicate-driven leftmost; The off-by-one death zone

  2. Binary search variants: `lower_bound`, `upper_bound`, peaks, and rotated arrays
    DSA Handbook - search, trees and heaps

    Variant 1: lower_bound and upper_bound on duplicates; Variant 4: binary search on the answer; Pitfalls

    Depth · Skim Variants 2, 3 and 5 (rotated arrays, peaks, cross-array partition) for recognition only; they are not implemented here.

  3. Lecture Note 12: the dictionary lower bound
    Algorithms

    Section 12.3 Information Theory

    Depth · Read only the dictionary lower-bound argument (Omega(log n) queries); the sorting sections belong to the merge-sort unit.

  4. Algorithmica: Binary Search
    Algorithmica - search and sorting performance

    Binary Search (The Bottleneck); Removing Branches

    Depth · Read for the branch-prediction and cache explanation only. Every speedup number there (2.5x to 3x branchless on small arrays, about 2x Eytzinger against std::lower_bound) is a C++ measurement with Clang 10 on Zen 2; do not transfer any crossover point to Python.

Focus on

  • Closed [lo,hi] pairs with while lo <= hi and hi=mid-1; half-open [lo,hi) pairs with while lo < hi and hi=mid. Mixing the pairs is the non-termination bug.
  • lower_bound keeps equality right with nums[mid] < target; upper_bound keeps equality left with nums[mid] <= target. Their difference counts duplicates.
  • Answer-space search is Template C with a monotone predicate: feasibility must hold above (or below) a threshold with no gaps. Cost is \(O(F \log W)\) for predicate cost \(F\) and width \(W\).
  • Algorithmica's C++ numbers explain why equal-asymptotic searches cross over (an unpredictable branch costs 10 to 15 cycles, a cache miss up to 300), but Python's interpreter overhead moves every crossover. Verify bounds against bisect, not against ported constants.

Reproduce closed-book

From a blank file, implement lower_bound, upper_bound and a first-true search on [lo,hi). On [1,2,2,2,3] with target 2, return 1 and 4. Reproduce Koko: piles [3,6,7,11] with h=8 must return 4. Run a Hypothesis differential test of both bounds against bisect_left and bisect_right on 300 sorted lists up to length 30.

Traps

  • Returning lo for every template. The upper-bound twin and Template A need their own return values; derive each from the invariant instead of memorizing one.
  • Writing lo=mid or hi=mid in Template A after a miss. The closed interval needs mid+1 and mid-1 to shrink; the half-open forms are the ones that keep mid.
  • Searching on a predicate that is not monotone. Prove hours(k) is nonincreasing in \(k\) (or your predicate's direction) before trusting the loop; a gap in feasibility silently returns a wrong answer.

Practice

LeetCode 875: Koko Eating Bananas

Solve with a monotone hours predicate on [1, max(piles)]; example piles [3,6,7,11] with h=8 expects 4

Attempt cold: state the predicate and its monotonicity before coding. After submitting, read the AlgoMaster write-up for Koko Eating Bananas (https://algomaster.io/learn/dsa/koko-eating-bananas).

LeetCode 34: Find First and Last Position of Element in Sorted Array

Two bound searches; example inputs ([5,7,7,8,8,10], 8) expects [3,4] and ([5,7,7,8,8,10], 6) expects [-1,-1]

Cover the empty array and the absent target without extra branches. After submitting, read the AlgoMaster write-up for Find First and Last Position of Element in Sorted Array (https://algomaster.io/learn/dsa/find-first-and-last-position-of-element-in-sorted-array).

Self-test

  1. Prove Template B terminates with lo at the lower bound.

  2. On [1,2,2,2,3], what do exact-match search, lower_bound and upper_bound return for target 2, and why?

  3. Why is binary search correct on the Koko predicate when speeds are not stored in an array?

  4. A colleague ports Algorithmica's branchless binary search to Python expecting 2.5x. What do you tell them?

Completion criterion

Stop when

Your three searches match bisect on 300 random cases, Koko returns 4, and you can write any bound variant from its invariant without trial edits.

Mark passed, then open 2.4 Only mark this when you can do it without notes.

Leads to

Comes after

Resources for this unit

  • DSA Handbook - search, trees and heaps
    Handbook Academyprimarycourse
    Study Part 2 Search and Sort, Part 5 Linked Lists, and Part 6 Trees and Heaps. Include canonical and answer-space binary search, comparison and linear-time sorts, quickselect, sentinels, cycles, heaps, BSTs, balanced-tree vocabulary, tries and segment trees.
  • Algorithmica - search and sorting performance
    Sergey Slotinsupportapplication
    Use the binary-search, sorting, branch prediction and cache chapters to explain why two algorithms with the same asymptotic bound cross over on real workloads.
  • Algorithms
    Jeff Ericksonreferencetheory
    Use Chapter 1 (mergesort, quicksort, recursion trees and linear-time selection) when deriving merge, partition and selection algorithms, and lecture note 12 Lower Bounds for the comparison-sorting decision-tree argument. This is the proof reference when the handbook explanation is too brief.

Also in this section

Quick find

Search Michi

Type to search every route, section, unit, topic and resource.

Enter opens the first result Esc closes

Reference

Keyboard shortcuts

n
Continue to the first unit not passed
j / k
Next / previous unit
1 2 3
Toggle learned, practised, passed
/
Open search
?
Show this help
Esc
Close open panels