Binary Search in Python
Learn Binary Search in Python! A super fun and beginner-friendly guide to finding elements in a sorted array blazing fast using iterative and recursive code.
Try it yourself
Run this code directly in your browser. Click "Open in full editor" to experiment further.
Click Run to see output
Or press Ctrl + Enter
How it works
Binary search turns a hopeless-looking search into a handful of steps. It's a classic divide and conquer technique that finds a target inside a sorted sequence by cutting the search space in half on every comparison. If linear search is checking every face in a crowd, binary search asks the crowd to line up by height and steps straight to the right spot. ๐ฏ
You'll see it everywhere โ coding interviews, the Python standard library, database indexes, even git.
How It Works
Think of a 1990s phone book. You want Salinger. You don't start at Aaronson and read forwards. You flop the book open near the middle, see M, and instantly know two things: Salinger is to the right, and the entire left half can be thrown away forever. Split the right half. T โ too far, drop it. Split again. Q. Then S. You converge on the page in roughly seven jumps instead of a thousand.
That's the whole intuition. Every step keeps three values:
left pointer at the start of the live rangeright pointer at the endmid computed safely as left + (right - left) // 2Compare arr[mid] to the target. Three outcomes: equal (done), too small (move left = mid + 1), too big (move right = mid - 1). Each comparison eliminates half the remaining candidates. After k comparisons, n items shrink to n / 2^k โ that's why the cost is log_2 n. A sorted array of one million items finishes in about 20 steps. A billion, about 30. That's logarithmic search in action.
The non-negotiable rule: the data must already be sorted. Run it on an unsorted array and it will cheerfully return wrong answers without complaining.
Iterative vs Recursive
The two implementations solve the same problem with different shapes.
The iterative version uses while left <= right: and updates pointers in place. It's what most Python developers reach for: constant extra memory, no stack risk. Python's own bisect module is implemented iteratively in C.
The recursive version expresses divide-and-conquer more literally โ each call solves a smaller subproblem of the same shape. It reads beautifully, but Python's default recursion limit is 1000, every call adds a stack frame, and there's no tail-call optimisation. For binary search this is rarely fatal, but it's pure overhead.
In interviews, write the iterative version unless asked otherwise. In real code, import bisect.
Complexity Analysis
| Variant | Best time | Avg / worst time | Space |
|---|---|---|---|
| Iterative | O(1) | O(log n) | O(1) |
| Recursive | O(1) | O(log n) | O(log n) |
| Linear search | O(1) | O(n) | O(1) |
Recursive pays O(log n) in stack space. Best case is O(1) for both โ the first mid might already be the answer.
Common Pitfalls
Most bugs are at the boundaries, not in the idea.
while left < right: when right = len(arr) - 1 silently skips the last element. The snippet above uses <= because right is an inclusive index. Pick one convention and stick to it.left + (right - left) // 2 is a habit worth keeping for portability.left = mid instead of left = mid + 1 (or right = mid instead of right = mid - 1) traps you on the same midpoint forever. Always exclude the midpoint once you've ruled it out.mid in a linked list is already O(n), which destroys the whole benefit.Real-World Uses
bisect_left, bisect_right, and insort give you ordered insertion and lookup with zero dependencies.WHERE id = 42 is a tree-shaped binary search.git bisect walks your history binary-search style, asking you to mark each midpoint commit good or bad until it pinpoints the culprit.Variants Worth Knowing
Leftmost / rightmost binary search. When duplicates exist, plain binary search returns some match. To get the first or last occurrence, keep searching after a match โ push right = mid - 1 for leftmost, left = mid + 1 for rightmost.
`lower_bound` / `upper_bound`. These return the insertion point that keeps a list sorted โ lower_bound is the first valid spot, upper_bound the last. Python's bisect_left and bisect_right are exactly these.
Search in a rotated sorted array. Given [6, 7, 0, 1, 2, 4, 5], find a target. At each step decide which half is still sorted, then check whether the target falls inside it. Still O(log n).
Binary search on the answer (parametric search). When a problem has a monotonic yes/no predicate โ can we ship all packages within `k` days? โ you binary search the answer space rather than an array. Turns gnarly problems into clean O(n log n) solutions.
Frequently Asked Questions
Why is binary search faster than linear search?
Linear search is O(n) โ one comparison per item. Binary search is O(log n) โ it discards half the candidates per comparison. For a million items, that's roughly 20 comparisons vs a million.
Does the array have to be sorted?
Yes, completely. The whole algorithm relies on the invariant that everything left of mid is <= arr[mid] and everything right is >=. Break it and the result is meaningless.
What if the array has duplicates?
Plain binary search returns some matching index โ no guarantee which one. For first or last occurrence, use the leftmost / rightmost variant, or bisect_left / bisect_right.
Is `bisect` faster than my hand-written version?
Nearly always. bisect is written in C and skips Python interpreter overhead. Roll your own to learn โ use bisect in production.
Can I binary search on floats?
Yes, and it's invaluable for things like square roots or finding a smallest feasible parameter. Loop a fixed number of times (say 100) or until right - left < 1e-9 instead of relying on equality, which rarely terminates cleanly with floats.
Can I use binary search on a linked list?
Not usefully. Reaching the midpoint already costs O(n) โ no random access. Use a sorted array, balanced BST, or skip list instead.
Related examples
Bubble Sort in Python
Learn the Bubble Sort algorithm in Python! Step-by-step visual explanation with beginner-friendly, optimized Python code.
Two Sum Algorithm in Python
Learn how to solve the classic Two Sum algorithm in Python! An incredibly fun tutorial on using Hash Maps to upgrade your code from slow O(n^2) to blazing fast O(n).
Merge Sort in Python
Learn Merge Sort in Python! A fun, beginner-friendly guide to the Divide and Conquer sorting strategy with guaranteed fast performance.