CS
Algorithms
Problems Base course RU

SRT Searching and sorting · Algorithm

Binary search

Also known as: bisection · lower_bound · git bisect

How to find the commit that broke everything among nine hundred in ten checks. Each question of a binary search throws away half of the suspects, and it works wherever the answer “yes” or “no” changes exactly once.

Basics 20 minutes searching binary search array version control classic interviews

Complexity

Time
O(log n)
Memory
O(1)

at most ⌈log₂(n + 1)⌉ checks: 20 for a million, 30 for a billion

Know first

Big O

In the base course

20. The sorting tournament

Where it runs

  • git bisect: finding the commit with a bug
  • database indexes and B-trees use the same trick inside a node
  • bisect in Python and sort.Search in Go
  • debugging any “it used to work, now it does not”

History

John Mauchly (first mention), 1946

Contents

  1. The idea
  2. Lower and upper bounds
  3. Why the loop is right and finite
  4. Code
  5. Bugs everyone makes
  6. What it costs
  7. History
  8. Problems

Yesterday everything worked, today it is broken, and hundreds of commits lie in between. Checking each is slow: build, run, look. But you do not need to check each. Take the middle: if it is already broken there, the culprit is in the first half; if not yet, in the second. Every check cuts off half of the suspects. Below is the real history of this site, 904 commits from February to October 2026. Pick a question—for example, when the maths textbook appeared—and see how many checks it takes.

Ten checks for 904 commits. Scanning from the first commit up to the maths textbook would take 484. On a million commits binary search would need twenty. This is the git bisect command: it works exactly like this, only the check is yours—you build the version it picked and say “good” or “bad”.

The idea

If the answer to “yes already?” changes exactly once along a row—first only “no”, then only “yes”—the boundary can be found by always asking about the middle of the remaining piece. One answer halves the piece, and after log₂ n questions a single point is left.

Note that this says nothing about numbers or sorting. All it needs is for the question to be monotone: if the textbook is in commit #483, it is in every later one too. A sorted array is a special case: for it the question “element ≥ x?” is monotone. So are “will k lorries be enough?”, “will the program fit in a second with n elements?”, “is the site broken in this version?”—and binary search works for all of them. The last case is covered in detail in binary search on the answer.

Lower and upper bounds

Most often binary search looks not for “is x in the array” but for a boundary: the first place where the element is not less than x. That position is called the lower bound. The first place where the element is strictly greater than x is the upper bound. Between them lie all the elements equal to x, and to the right of the lower bound lie all the elements not less than x.

Here are all the earthquakes of magnitude 5 and above in 2015–2025 from the USGS catalogue—19,073 of them, sorted by magnitude. How many were stronger than 7? Move the threshold and watch lo and hi close in on the answer.

The shaded band is everything we know nothing about yet. The code keeps one statement, an invariant: all elements left of lo are below the threshold, all elements from hi on are not. At the start lo = 0 and hi = n, and the statement holds because both sides are empty. Every step looks at the middle and moves one of the bounds so that the statement still holds. When lo and hi meet, nothing unknown is left, and lo is the answer.

Why the loop is right and finite

Take the middle mid = ⌊(lo + hi)/2⌋. While lo < hi, it lies in the half-open interval [lo, hi): mid ≥ lo and mid < hi.

  • If a[mid] < x, every element left of mid is less than x too—the array is sorted. Move lo = mid + 1: the invariant “everything left of lo is less” still holds.
  • Otherwise a[mid] ≥ x, and so is everything to its right. Move hi = mid: “everything from hi on is not less” still holds.

Either way the length hi − lo strictly decreases: in the first case lo grew by at least one, in the second hi became smaller than before. So the loop ends, and since the length nearly halves each time, there are at most ⌈log₂(n + 1)⌉ steps.

This is where almost every bug hides: write lo = mid instead of lo = mid + 1, and when hi = lo + 1 the middle equals lo, the length stops shrinking, and the loop never ends.

Code

Both bounds on the half-open interval [lo, hi). It is the most reliable form: the answer n (“no such element”) comes out by itself, with no special cases.

Standard libraries already have this. In Python, the bisect module: bisect_left(a, x) is the lower bound, bisect_right(a, x) the upper one. In Go, sort.Search(n, f): it finds the first index at which the function f becomes true, so it works with any monotone question, not only an array. For slices there is also slices.BinarySearch.

And this is git bisect over the site's history. Checking a commit here is a recorded answer rather than a build, but the search is the same:

Bugs everyone makes

Jon Bentley wrote in “Programming Pearls” about giving professional programmers on courses at Bell Labs and IBM the task of writing a binary search. They had plenty of time, yet only about one in ten ended up with correct code. The bugs repeat year after year:

  • An endless loop. lo = mid with mid rounded down: on a piece of two elements the search stands still.
  • Mixed conventions. A half-open [lo, hi) in one place and a closed [lo, hi] in another: then the loop either skips the last element or reads past the end of the array. Pick one convention and stick to it.
  • Overflow. In Java and C, (lo + hi) / 2 overflows when lo + hi exceeds 2³¹ − 1. Such a bug lived for nine years in Arrays.binarySearch in Java's standard library until Joshua Bloch wrote about it in 2006. The safe form is lo + (hi - lo) / 2. Python integers do not overflow and Go's int is 64 bits, but the habit is a good one.
  • Unsorted data. A binary search over an unsorted array finds nothing and reports no error—it just returns nonsense. If the array changes, sort it again or keep it in a search tree.

What it costs

Each check is a “yes” or “no”, exactly one bit of information. To tell n + 1 possible answers apart you need at least log₂(n + 1) bits, so binary search is optimal among all methods that learn about the data only through comparisons. Memory: a couple of variables.

ElementsChecks by scanning (worst case)Checks by binary search
904 commits90410
19,073 earthquakes19,07315
a million1,000,00020
a billion1,000,000,00030

There is a price too: the data must be ordered. Sorting costs O(n log n), and if you search only once, it is simpler to scan the array in O(n). Binary search wins when there are many searches, or when the order is there anyway—like commits in time.

History

The idea of binary search over a sorted table was mentioned by John Mauchly in the Moore School Lectures of 1946—one of the first programming courses in history. But, as Donald Knuth noted in “The Art of Computer Programming”, a version that works correctly for a table of any length appeared in print years later. A short algorithm that everyone understands at first sight turned out to be one of the hardest to write without bugs.

Problems

The first is lower and upper bounds on the same earthquakes. In the second the binary search runs not over an array but over numbers, and it is easy to fall into the floating-point trap.

Earthquakes in a range The k-th root

Data: Commit history of legost.in Earthquakes: USGS catalogue, public domain

See also

SRT Technique Binary search on the answer If checking an answer is easier than finding it, and the check is monotone, search for the answer by halving. O(log D · cost of a check) 2 problems DS Data structure Binary search tree Smaller to the left, larger to the right: search, order and the k-th element in the tree's height, from log n to n. O(h): from O(log n) to O(n) 2 problems
← Back to the catalogue The algorithms catalogue Next → Searching and sorting Binary search on the answer

Written by Viacheslav Legostin. Everything is free; readers’ code is not stored.

Computer Science The algorithms catalogue Problems Base course Mathematics legost.in

The algorithms catalogue

Contents

  1. The idea
  2. Lower and upper bounds
  3. Why the loop is right and finite
  4. Code
  5. Bugs everyone makes
  6. What it costs
  7. History
  8. Problems

Problems

SRT Searching and sorting

  1. Binary search
  2. Binary search on the answer
  3. Counting sort and radix sort

DS Data structures

  1. Dynamic array
  2. Cuckoo hashing
  3. Bloom filter
  4. Binary heap
  5. Binary search tree
  6. AVL tree
  7. B-tree
  8. Disjoint set union

GRA Graphs

  1. Breadth-first search
  2. Depth-first search
  3. Bridges and articulation points
  4. Dijkstra's algorithm
  5. A*
  6. Bidirectional search
  7. The Bellman–Ford algorithm
  8. Contraction hierarchies
  9. Dinic's algorithm
  10. Bipartite matching
  11. Centroid decomposition

STR Strings and text

  1. The Aho–Corasick algorithm
  2. Boyer–Moore
  3. The Burrows–Wheeler transform
  4. Edit distance
  5. BK-tree

DP Dynamic programming

  1. Dynamic programming
  2. Bitmask DP
  3. Digit DP
  4. Convex hull trick
  5. Knuth and divide-and-conquer optimisations

PAR Paradigms

  1. Backtracking
  2. Branch and bound
  3. Divide and conquer
  4. Closest pair of points

NUM Numbers and algebra

  1. Chinese remainder theorem
  2. Big integers

GEO Geometry

  1. Convex hull
  2. Delaunay triangulation and Voronoi diagram

NUM~ Numerical methods

  1. Bisection and Newton's method

SYS Systems and big data

  1. Consistent hashing
  2. Count-Min Sketch
  3. Retry with jitter
  4. CRDT

ML Data and machine learning

  1. k-means
  2. DBSCAN
  3. Decision tree
  4. Perceptron and backpropagation

TEC Techniques

  1. Big O
  2. How to benchmark honestly
  3. Cache-friendly data
  4. Complexity from measurements
  5. Amortized analysis
  6. Choosing a data structure
  7. Coordinate compression
  8. Bit tricks