Core Idea: Eliminate half the search space each iteration by comparing mid to target or checking a predicate.
O(log n) for search space of size n. Reduces n → n/2 → n/4 → ... → 1
Goal: Find target in sorted array, return index or -1.
Why Binary Search? Array is sorted. Compare mid with target to eliminate half.
Key Insight: If nums[mid] < target, eliminate left half (target must be right). If nums[mid] > target, eliminate right half.
Loop Condition:
Use l <= r to ensure mid is checked when l == r.Goal: Search sorted 2D matrix where each row is sorted and first element of each row > last element of previous row.
Why Binary Search? Matrix properties guarantee each row is sorted independently. Can binary search to find correct row, then binary search within that row.
Key Insight: Check if row[0] <= target <= row[n-1] to identify correct row. Then standard binary search within row.
Alternative:
Treat as flattened 1D array. Convert 1D index to 2D: row = idx / cols, col = idx % cols.Goal: Find minimum eating speed k to finish all banana piles within h hours.
Why Binary Search? We're not searching an array - we're searching the answer space [1, max(piles)]. Define predicate P(k) = "can finish in h hours at speed k". P(k) is monotonic: if P(k) true, then P(k+1) also true.
Key Insight: Search for minimum k where P(k) is true. This is finding the boundary where false → true. When P(mid) is true, search left for smaller k. When false, search right for larger k.
Binary Search on Answer:
When optimizing "minimize k such that P(k) is true", search answer space [min, max] instead of array indices.Return Value:
Return l (not r) - l converges to first valid answer where P(k) flips from false to true.Goal: Find minimum element in sorted array that has been rotated.
Why Binary Search? Rotated sorted array has two sorted segments. Minimum is at the start of the lower segment. Compare nums[mid] with nums[r] to determine which segment mid is in.
Key Insight: If nums[mid] < nums[r], mid is in the lower sorted segment, so minimum is in [l, mid]. If nums[mid] >= nums[r], mid is in upper segment, minimum is in [mid+1, r].
Why Compare with Right?
Comparing with nums[r] tells us if we're in the rotated portion. If nums[mid] > nums[r], there's a rotation between mid and r.Loop Condition:
Use l < r (not l <= r) since we're finding minimum index, not searching for target.Goal: Store values with timestamps and retrieve most recent value ≤ query timestamp.
Why Binary Search? Timestamps are strictly increasing. Want largest timestamp ≤ query. This is "find last occurrence" variant of binary search.
Key Insight: When data[mid].timestamp <= query, it's valid - save it and search right for potentially larger valid timestamp. When > query, search left.
Finding Last Valid:
When condition is true, move l = mid+1 (search right). When false, move r = mid-1. Return the saved answer, not l or r.Use When: Searching for target in sorted array.
Template: if nums[mid] == target return mid, else adjust l/r.
Use When: Optimizing "minimize/maximize k such that P(k) is true".
Template: Define predicate P(k), search [min_k, max_k] for boundary.
Use When: Sorted array has been rotated.
Template: Compare mid with boundary to identify which segment.
First: When condition true, move r = mid-1, return l.
Last: When condition true, move l = mid+1, return saved answer.
Goal: Find median of two sorted arrays without merging them.
Can't merge arrays (O(n) time). Must use binary search, but not searching for a value - searching for the correct partition of both arrays that creates a valid left/right split where all left elements < all right elements.
Median means creating a symmetrical split where left half = right half in size (or left has 1 more if odd total).
Key Insight: We need to decide how many elements from each array go into the left partition. If total is 10, we need 5 on left. Maybe 3 from arr1, 2 from arr2. Or 2 from arr1, 3 from arr2. There's exactly ONE valid configuration.
Total = 10 (even), need 5 on left, 5 on right.
Symmetry Check:
Cross-compare maximums of left with minimums of right. If both cross-checks pass, all left < all right.Instead of searching array values, we search "how many elements from arr1 go to left partition". Range: [0, len(arr1)].
Always search on the smaller array for better time complexity: O(log(min(m,n))).
When partition is invalid (symmetry broken), we know which direction to search:
n1=4, n2=6, total=10 (even), left_size=5
Why +1? Works for both even and odd totals. If total=10, gives 5. If total=11, gives 6 (left has 1 more).
Odd total (left has 1 extra):
The maximum of the left partition is the middle element.
Even total (equal split):
Average of largest left element and smallest right element.
When i=0 (no elements from arr1 on left), aLeft = -∞ so it doesn't affect max().
When i=n1 (all elements from arr1 on left), aRight = +∞ so it doesn't affect min().
If we know the correct left partition, we automatically know the right partition (everything else).
By using (n1+n2+1)/2, we ensure:
This formula handles both cases uniformly in the binary search loop.
We don't need to check aLeft <= aRight or bLeft <= bRight because arrays are already sorted internally.
We ONLY check the cross boundaries: aLeft <= bRight and bLeft <= aRight. These are the "walls" between our partition.
1. Not searching on smaller array:
Time complexity becomes O(log(max(m,n))) instead of O(log(min(m,n))).2. Wrong partition size formula:
Using n/2 instead of (n+1)/2 breaks odd case.3. Forgetting sentinels:
When i=0 or j=0, arrays have no left element. When i=n1 or j=n2, no right element. Must use -∞/+∞.4. Wrong elimination logic:
If aLeft > bRight, must reduce i (fewer from arr1). If bLeft > aRight, must increase i (more from arr1).5. Integer division for median:
For even totals, remember to divide by 2.0 (float) not 2 (int) to get correct decimal result.