Overview
Binary search finds a target (or its correct insertion point) in a sorted sequence by repeatedly comparing against a midpoint and discarding the half that can't contain the answer, which is what makes it O(log n) instead of O(n), each comparison eliminates half the remaining space rather than checking one element at a time. That halving logic is only valid because the data is sorted; run it on unsorted input and there's no error, the algorithm simply has no way to detect the broken invariant and returns a result with no real relationship to the actual data. Python's bisect_left and bisect_right both do this same search but define slightly different insertion points for a value that already appears in the list, before or after the existing equal entries, which matters directly for keeping equal-valued elements grouped consistently on repeated insertion.
Quick Reference
| Function | Insertion point for an existing value | Guarantee |
|---|---|---|
bisect_left | Before all existing equal entries | Everything before the index is strictly < target |
bisect_right (bisect) | After all existing equal entries | Everything before the index is <= target |
Syntax
import bisect
sorted_list = [1, 2, 2, 2, 3]
bisect.bisect_left(sorted_list, 2) # 1
bisect.bisect_right(sorted_list, 2) # 4Examples
# Binary search on unsorted data doesn't error; it just returns
# a meaningless result, since the halving logic assumes order.
import bisect
unsorted = [5, 1, 4, 2, 3]
bisect.bisect_left(unsorted, 3)
# No exception - but this index has no real meaning here.Binary search fails silently on unsorted input
There's no runtime check for sortedness, an unsorted list produces a wrong-but-plausible-looking result rather than an error. The invariant is the caller's responsibility to maintain, not something the algorithm verifies.
Visual Diagram
Common Mistakes
- Running binary search (or
bisect) on data that isn't actually sorted and trusting the result, since nothing detects or errors on the broken invariant. - Confusing
bisect_leftandbisect_rightwhen the exact insertion position relative to existing equal values matters (e.g., maintaining insertion order among duplicates). - Applying binary search to a linked list or other structure without O(1) random access, losing the whole advantage since reaching the midpoint itself costs O(n).
- Forgetting binary search needs the comparison direction consistent with how the data is actually sorted (ascending vs. descending).
Performance
- Binary search's O(log n) advantage over a linear O(n) scan is dramatic at scale, roughly 20 comparisons for a million-element sorted array versus up to a million.
- That advantage depends entirely on O(1) random access to the midpoint; on a data structure without it, the navigation cost can dominate and erase the algorithmic benefit.
Best Practices
- Verify data is actually sorted before relying on binary search, since there's no runtime check that will catch the mistake for you.
- Choose
bisect_leftorbisect_rightdeliberately based on whether new equal-valued entries should land before or after existing ones. - Reach for binary search specifically on structures with O(1) random access (arrays, lists); it doesn't deliver its usual advantage on linked structures.
- Prefer a language's built-in binary search implementation (like
bisect) over hand-rolling one, off-by-one errors in manual binary search are a classic, well-documented source of bugs.