Binary search extends far beyond locating values in sorted arrays. The core requirement for applying this technique is identifying a monotonic relationship between an independent variable and a computed result. When a problem can be modeled as finding an input x such that a monotonic function f(x) equals a specific target, binary search becomes applicable.
Most practical algorithmic challenges require finding boundary values rather than exact matches. Optimization problems asking for minimum capacity, maximum speed, or earliest time inherently translate to searching for the leftmost or rightmost valid x that satisfies a condition.
Left Boundary Search Pattern
When the objective is to find the smallest x where f(x) >= target (or exactly equals it in a discrete domain), the search space must contract from the right upon finding a valid candidate.
int findLeftBoundary(const std::vector<int>& data, int goal) {
if (data.empty()) return -1;
int low = 0;
int high = data.size();
while (low < high) {
int pivot = low + ((high - low) >> 1);
int val = data[pivot];
if (val == goal) {
high = pivot;
} else if (val < goal) {
low = pivot + 1;
} else {
high = pivot;
}
}
return low;
}
Right Boundary Search Pattern
Conversely, locating the largest valid x requires pushing the lower bound upward when a match occurs, effectively searching for the insertion point immediately after the last valid element.
int findRightBoundary(const std::vector<int>& data, int goal) {
if (data.empty()) return -1;
int low = 0;
int high = data.size();
while (low < high) {
int pivot = low + ((high - low) >> 1);
int val = data[pivot];
if (val == goal) {
low = pivot + 1;
} else if (val < goal) {
low = pivot + 1;
} else {
high = pivot;
}
}
return low - 1;
}
Abstracting the Monotonic Function
The array index serves as the independent variable x, and array access acts as the function f(x) = data[x]. Since the array is sorted, f(x) is strictly monotonic. The problem of finding the first occurrence of goal translates to finding the minimum x where f(x) == goal.
This abstraction unlocks binary search for non-array problems. Any scenario where increasing x consistently increases or decreases a computed metric f(x) fits this model. The algorithm searches the domain of x rather than a physical data structure.
int evaluate(int x) {
// Compute metric based on x
// Must be strictly monotonic (increasing or decreasing)
return computed_value;
}
int solveBoundary(int min_x, int max_x, int goal) {
int low = min_x;
int high = max_x + 1;
while (low < high) {
int pivot = low + ((high - low) >> 1);
int result = evaluate(pivot);
if (result == goal) {
high = pivot; // Adjust to low = pivot + 1 for right boundary
} else if (result < goal) {
low = pivot + 1;
} else {
high = pivot;
}
}
return low;
}
Systematic Application Framework
Implementing binary search for complex constraints follows a deterministic sequence:
- Identify Variables: Isolate the independent variable
x, define the monotonic mappingf(x), and establish thetargetconstraint. Implementf(x)as a standalone function or lambda. - Define Search Space: Determine the absolute minimum and maximum possible values for
x. Initializelowandhighto cover this inclusive range, typically settinghightomax_x + 1for half-open interval logic. - Select Boundary Logic: Decide whether the problem requires the smallest valid
x(left boundary) or largest validx(right boundary). Adjust thelowandhighpointers inside the loop accordingly, ensuring the loop terminates whenlow == high.