Binary Search finds a target value in a sorted array by repeatedly dividing the search interval in half, eliminating half of the remaining elements each time.
- The data is sorted (or you can afford to sort it once before many queries).
- You need faster than linear search (
O(n)) – Binary Search runs inO(log n). - You are searching for a condition that changes from “false” to “true” at a certain point (e.g., first defect, first index where
arr[i] >= target). - The answer lies within a known range (integers) and you can check feasibility – search over output space (e.g., Koko Eating Bananas, capacity to ship packages).
- Start with two pointers:
leftat the beginning of the sorted list,rightat the end. - While
leftis less than or equal toright(orleft < rightdepending on what you need):- Compute the middle index:
mid = left + (right - left) // 2(avoids overflow). - Compare the value at
midwith your target. - If
arr[mid] == target→ found, returnmid. - If
arr[mid] < target→ target lies to the right, so moveleft = mid + 1. - If
arr[mid] > target→ target lies to the left, so moveright = mid - 1.
- Compute the middle index:
- If the loop ends without finding the target, return a sentinel (e.g.,
-1) or the insertion point.
Think of it like looking up a word in a dictionary – you don’t check every page; you open the middle, decide whether to go left or right, and repeat.
def binary_search(nums: list[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1def lower_bound(nums: list[int], target: int) -> int:
"""Return first index i such that nums[i] >= target, or len(nums) if all < target."""
left, right = 0, len(nums)
while left < right:
mid = left + (right - left) // 2
if nums[mid] < target:
left = mid + 1
else:
right = mid
return leftdef binary_search_on_answer(low: int, high: int, feasible: callable) -> int:
"""Return smallest value in [low, high] for which feasible(x) is True."""
while low < high:
mid = low + (high - low) // 2
if feasible(mid):
high = mid
else:
low = mid + 1
return lowimport bisect
arr = [1, 3, 3, 5, 6, 8, 17]
idx = bisect.bisect_left(arr, 3) # first index with arr[i] >= 3 → 1
idx2 = bisect.bisect_right(arr, 3) # first index with arr[i] > 3 → 3- Time:
O(log n)– each step halves the search space. - Space:
O(1)(iterative version) orO(log n)recursion stack (not shown, but avoid recursion for large n).
- Why must the array be sorted for Binary Search to work?
- What happens if you use
left < rightinstead ofleft <= rightin the standard version? - In the general feasibility template, what does
feasible(mid)represent?
Answers
- Binary Search relies on ordering to decide whether to go left or right. Without sorting, the comparison tells you nothing about where the target might be.
left < rightwill never examine the last remaining element whenleft == right, potentially missing the target if it is at that last index.feasible(mid)is a function that returnsTrueifmidsatisfies some condition (e.g., enough capacity, first defect). We search for the firstmidwhere it becomesTrue.
- Binary Search – classic exact value search.
- Search Insert Position – use lower bound.
- Find First and Last Position of Element in Sorted Array – combine
bisect_leftandbisect_right. - Koko Eating Bananas – search over output space (eating speed).
- Find Minimum in Rotated Sorted Array – binary search with a twist.
- H-Index II – search on a sorted citations array.
For a long time I was confused about when to use left <= right versus left < right.
I learned that:
- Use
left <= rightwhen you want to search for an exact match and you need to examine the element whenleft == right. - Use
left < rightwhen you are trying to narrow down to a single candidate (like the lower bound or feasibility search) and you always keep the answer inside[left, right].
The loop invariant is key – practicing each variant on simple examples (e.g., array of length 1, target not present) helped me finally get it.
- Linear Search – the naive alternative.
- Ternary Search – for unimodal functions (less common).
- bisect module documentation