Binary Search
Binary search finds a boundary in an ordered candidate space. The key is a monotonic predicate: once a candidate is classified as “after,” every later candidate must also be “after.”
1. The Transition-Point Recipe
Reframe the search space as two monotonic regions:
before before before before | after after after
^
transition point
Define is_before(x) so every “before” candidate comes before every “after” candidate. The values themselves do not need to be booleans; only this classification must be monotonic.
def first_after(first, last, is_before):
if first > last or not is_before(first):
return first
if is_before(last):
return last + 1
left, right = first, last
while right - left > 1:
mid = left + (right - left) // 2
if is_before(mid):
left = mid
else:
right = mid
return right
The loop never changes:
leftremains known “before,” andrightremains known “after.”midis strictly between them, so every iteration makes progress.- When they are adjacent,
leftandrightidentify the transition.
The function searches inclusive integer bounds [first,last] and returns the first after index, or last+1 if all candidates are before. Empty input returns first. For a lower bound, call first_after(0, len(nums)-1, lambda i: nums[i] < target); check the returned index is in bounds before checking equality. For an upper bound, use nums[i] <= target instead. Subtract lower from upper to count occurrences.
2. Translate the Task into a Predicate
| Task | “Before” condition | Returned boundary |
|---|---|---|
| First positive value in a sorted array | nums[i] <= 0 | First positive index, or n if absent |
| Minimum of a nonempty rotated sorted array with distinct values | nums[i] > nums[-1] | Start of the lower-valued suffix; index zero if unrotated |
Smallest capacity that permits at most k groups | groups_needed(capacity) > k | First feasible capacity within the chosen bounds |
For the rotated minimum, values before the pivot exceed the last value and values from the pivot onward do not. Duplicates can destroy that separation: [1,0,1] is a counterexample. For capacity search, proving that required group count never increases with capacity is part of the solution, not an assumption supplied by the binary-search loop.
3. Reusable Idea: Grid Flattening
If an R x C grid is sorted in complete row-major order, imagine it as one virtual sorted array of length R * C. Do not allocate that array; translate indices only when reading a cell:
- Grid to virtual index:
i = r * C + c. - Virtual index to grid:
r = i // C,c = i % C.
This produces an search with extra space. It is valid only when each row is sorted and the last value of one row is smaller than the first value of the next row.
4. Reusable Idea: Exponential Search and Guess-and-Check
When a search range has no known upper bound, probe 1, 2, 4, 8, ... until the predicate changes. The last two probes bracket the transition; then apply the normal recipe inside that interval.
left, right = 0, 1
while is_before(right):
left = right
right *= 2
# The transition is now in (left, right].
answer = first_after(left, right, is_before)
This pairs naturally with guess-and-check:
- Guess a candidate answer.
- Check whether it is too small, too large, feasible, or infeasible.
- Prove those answers form one monotonic transition.
- Binary search for the first or last candidate that satisfies the constraint.
Use this when checking one candidate is much easier than constructing the optimum directly. If the answer bounds are unknown, exponential search can discover them first.
5. Complexity and Key Takeaways
A range of N candidates needs predicate evaluations. If a feasibility check scans n items, total time is , not just logarithmic. Auxiliary space includes whatever the predicate allocates.
- State the first-after return convention and check the
last+1sentinel before indexing. - Empty, all-before, all-after, one-item and duplicate-heavy inputs must follow the same contract.
- A globally sorted flattened grid works; a grid with only each row sorted does not give one global transition.
- Repeated doubling needs a guaranteed finite transition or a known limit. Handle index overflow and out-of-range probes; otherwise bound discovery may never stop.
For splitting a nonnegative array into at most k groups while minimizing the maximum group sum, a guessed capacity can be checked by greedily filling each group. More capacity never requires more groups, so binary search can find the smallest feasible capacity. Prove that monotonicity for each new problem; an arbitrary local trend or a rotated array with ambiguous duplicates may require different handling.
Continue to Two Pointers when candidates can be eliminated one at a time, Sorting to establish order, and Dynamic Programming when a feasibility check itself has overlapping subproblems.
Practice Problems
Core Practice
Binary Search
Given a sorted integer array nums and an integer target, return the index of target in nums, or -1 if it does not exist.
Search Insert Position
Given a sorted array of distinct integers nums and a target value target, return the index if the target is found. If not, return the index where it would be inserted in order.
Search in Rotated Sorted Array
There is an integer array nums sorted in ascending order (with distinct values), and rotated. Given the array and an integer target, return the index of target or -1 if it is not found.
Min-Subarray-Sum Split
Given a non-empty array with n positive integers, arr, and a number k with 1 <= k <= n, the goal is to split arr into k non-empty subarrays so that the largest sum across all subarrays is minimized. Return the largest sum across all k subarrays after making it as small as possible. Each subarray must contain at least one value.
Optional Variations (10)
Choose a variation when you want to explore a specific pattern. These are not required before moving to the next topic.
Find Minimum in Rotated Sorted Array
Suppose an array of length n sorted in ascending order is rotated between 1 and n times. Given the rotated array nums of unique elements, return the minimum element.
Valley Bottom
A valley-shaped array is an array of integers such that:
2-Array 2-Sum
You are given two non-empty arrays of integers, sorted_arr and unsorted_arr. The first one is sorted, but the second is not. The goal is to find one element from each array with sum 0. If you can find them, return an array with their indices, starting with the element in sorted_arr. Otherwise, return [-1, -1]. Use O(1) extra space and do not modify the input.
Target Count Divisible By K
Given a sorted array of integers, arr, a target value, target, and a positive integer, k, return whether the number of occurrences of the target in the array is a multiple of k.
Race Overtaking
You are given two arrays of positive integers, p1 and p2, representing players in a racing game. The two arrays are sorted, non-empty, and have the same length, n. The i-th element of each array corresponds to where that player was on the track at the i-th second of the race. We know that:
Search in Sorted Grid
You're given a 2D grid of integers, grid, where each row is sorted (without duplicates), and the last value in each row is smaller than the first value in the following row. You are also given a target value, target. If the target is in the grid, return an array with its row and column indices. Otherwise, return [-1, -1].
Search in Huge Array
We are trying to search for a target integer, target, in a sorted array of integers (duplicates allowed) that is too big to fit into memory. We can only access the array through an API, fetch(i), which returns the value at index i if i is within bounds or -1 otherwise. Using as few calls to the API as possible, return the index of the target, or -1 if it does not exist. If the target appears multiple times, return any of the indices. There is no API to get the array's length.
Water Refilling
We have an empty container with a capacity of a gallons of water and another container with a capacity of b gallons. Return how many times you can pour the second container full of water into the first one without overflowing. Assume that a > b.
Min Pages Per Day
You have upcoming interviews and have selected specific chapters from BCtCI to read beforehand. Given an array, page_counts, where each element represents a chapter's page count, and the number of days, days, until your interview, determine the minimum number of pages you must read daily to finish on time. Assume that:
Tide Aerial View
You are provided a series of aerial-view pictures of the same coastal region, taken a few minutes apart. Each picture consists of an n x n binary grid, where 0 represents land (above water) and 1 represents water (below water).