Binary Search
Difficulty: Easy. ROI: Medium.
Other common names: Binary search on the answer (for search-space use).
When to use: Use when the array (or range) is sorted and you need to find a value, first/last occurrence, or the smallest value that satisfies a condition. Also “binary search on the answer” when the answer is in a range and you can test feasibility.
How Binary Search is different than Two Pointers:
- The two indices only mark the range; the decision is always at the middle (you compare
arr[mid]to the target, not the two ends). - Each step discards half the range — that’s why you get O(log n). Two pointers typically move one step at a time (O(n)).
- Binary search finds a single value (or first/last occurrence, or “smallest x such that …”). Two pointers work with pairs or two active positions (e.g. two numbers that sum to target).
- In short: binary search uses “two indices as bounds” and always inspects the middle; two pointers use two indices as the active positions and move them one step at a time.
Idea: Maintain a range [lo, hi] that must contain the answer. Each step compare the middle element to the target (or test mid for feasibility) and narrow the range to the left or right half.
Time / space: O(log n) time, O(1) extra space.
Example #1 — Find Target in Sorted Array
Section titled “Example #1 — Find Target in Sorted Array”Sample input and output
arr = [1, 3, 5, 7, 9]print(binary_search(arr, 5)) # Trueprint(binary_search(arr, 4)) # False- Sample input:
arr = [1, 3, 5, 7, 9],target = 5. - Sample output:
True— 5 is in the array. Fortarget = 4the output isFalse(not in array).
Code (with inline comments)
def binary_search(arr, target): lo, hi = 0, len(arr) - 1 # range that could contain the answer
while lo <= hi: mid = (lo + hi) // 2 # middle index (integer division) if arr[mid] == target: return True if arr[mid] < target: lo = mid + 1 # target must be in the right half else: hi = mid - 1 # target must be in the left half return FalseLine-by-line:
lo, hi = 0, len(arr) - 1— We keep a range [lo, hi] (inclusive) where the target could be. Initially the whole array.while lo <= hi:— If lo passes hi, the range is empty and the target isn’t in the array.mid = (lo + hi) // 2— Integer division (//) gives a whole number. We check the middle element of the current range.if arr[mid] == target: return True— Found it.if arr[mid] < target: lo = mid + 1— The array is sorted, so if the middle value is smaller than the target, the target must be to the right. We shrink the range to [mid+1, hi].else: hi = mid - 1— The middle value is greater than the target, so the target must be to the left. We shrink to [lo, mid-1].return False— The loop ended without finding the target.
Execution trace (numbered list)
For arr = [1, 3, 5, 7, 9], target = 5:
- Step 1: lo=0, hi=4, mid=2, arr[mid]=5. 5 == 5 → return True.
For a miss (e.g. target=4): Step 1: lo=0, hi=4, mid=2, 5>4 → hi=1. Step 2: lo=0, hi=1, mid=0, 1<4 → lo=1. Step 3: lo=1, hi=1, mid=1, 3<4 → lo=2. lo>hi → return False.
Execution trace (code with step comments)
arr = [1, 3, 5, 7, 9]target = 5lo, hi = 0, len(arr) - 1 # 0, 4
# Step 1mid = (lo + hi) // 2 # 2# arr[2] == 5 == target → return TrueFor target=4: mid=2, 5>4 → hi=1; mid=0, 1<4 → lo=1; mid=1, 3<4 → lo=2; lo>hi → return False.
Execution trace (ASCII diagram)
Range [lo, hi] and mid at each step; ^ marks mid.
arr = [ 1, 3, 5, 7, 9 ], target = 5 lo mid hi [-------^-------] arr[mid]=5 == 5 → return True