Python Tutorial
Binary Search
Search a sorted list in O(log n) by repeatedly halving the range that could contain the target.
The Divide-and-Conquer Idea
Binary search requires sorted data. It compares the target to the middle element: if equal, done; if the target is smaller, discard the right half; if larger, discard the left half. Each step throws away half the remaining elements, giving O(log n) — searching a million items takes about 20 comparisons.
Iterative Implementation
def binary_search(arr, target):
low, high = 0, len(arr) - 1
while low <= high:
mid = (low + high) // 2 # safe in Python (no overflow)
if arr[mid] == target:
return mid
elif arr[mid] < target:
low = mid + 1 # target is in the right half
else:
high = mid - 1 # target is in the left half
return -1
data = [11, 12, 22, 25, 34, 64, 90] # must be sorted!
print(binary_search(data, 25)) # 3
print(binary_search(data, 30)) # -1Binary search on unsorted data gives wrong answers. Sort first (O(n log n)) — worthwhile only if you search many times.
Recursive Version
def binary_search_rec(arr, target, low, high):
if low > high:
return -1
mid = (low + high) // 2
if arr[mid] == target:
return mid
if arr[mid] < target:
return binary_search_rec(arr, target, mid + 1, high)
return binary_search_rec(arr, target, low, mid - 1)
print(binary_search_rec(data, 64, 0, len(data) - 1)) # 5Use the Standard Library: bisect
Python's bisect module implements binary search in C. It finds insertion points, which you can use to test membership or keep a list sorted.
import bisect
data = [11, 12, 22, 25, 34, 64, 90]
i = bisect.bisect_left(data, 25)
found = i < len(data) and data[i] == 25
print(i, found) # 3 True
bisect.insort(data, 30) # insert 30 keeping the list sorted
print(data) # [11, 12, 22, 25, 30, 34, 64, 90]Linear vs Binary Search
| Linear Search | Binary Search | |
|---|---|---|
| Data requirement | Any order | Must be sorted |
| Time | O(n) | O(log n) |
| 1,000,000 items (worst) | ~1,000,000 checks | ~20 checks |
Best Practices
- Ensure the data is sorted before searching.
- Use
low + (high - low) // 2in languages prone to integer overflow (not needed in Python). - Prefer the
bisectmodule in production code. - Binary search generalizes: use it to find the first/last item satisfying a condition, or to search an answer space.
Try It Yourself
Exercise 1: Roughly how many comparisons does binary search need for 1,000,000 sorted items?
Show solution
About 20 (log₂ of 1,000,000 ≈ 20).
Exercise 2: Use the bisect module to test if 7 is in a sorted list.
Show solution
import bisect
a = [1, 3, 5, 7, 9]
i = bisect.bisect_left(a, 7)
print(i < len(a) and a[i] == 7) # True📘 Real-World Deep Dive
Knowing <strong>DSA Binary Search (algorithms & data structures)</strong> well is what turns algorithms & data structures from a curiosity into a daily tool — you'll reach for it in nearly every real project.
Real-Life Scenario
An end-to-end usage of DSA Binary Search that you'd actually see in a data pipeline or analytics notebook.
Real-Life Example
import bisect
xs = [1, 3, 5, 7, 9, 11, 13]
print("left 5:", bisect.bisect_left(xs, 5)) # 2
print("right 5:", bisect.bisect_right(xs, 5)) # 3Expected Output
(see source)Common mistakes
- Off-by-one errors in binary-search: the standard idiom is
while lo <= hiwithmid = (lo + hi) // 2. - Recursive algorithms blow the stack for n > ~10⁴; convert to iterative with an explicit stack.
- Comparison algorithms (
sorted(iterable)) are stable by default in Python — surprising for Java/C++ users. - Treating DSA Binary Search as a black box without reading the docs — the API has subtle defaults that bite when you scale.
🚀 Performance & Best Practices
- Use
bisect.bisect_left/bisect_rightinstead of writing your own binary search. - Convert a sorted search into a tuple-access pattern with
numpy.searchsortedfor huge arrays. - Use
heapqfor priority queues instead of maintaining a sorted list manually. - When working with algorithms & data structures, prefer vectorised / batched operations over Python loops.
🧪 Try It Yourself
- Reproduce the snippet on a representative slice of your own data.
- Profile the snippet with
cProfileortimeitand find the single biggest improvement. - Generalise the snippet into a small, reusable function you can drop into future projects.