When developers first encounter binary search, they learn it as an efficient $O(\log n)$ algorithm for finding an element in a sorted array. However, limiting binary search to searching through pre-existing index arrays severely limits its utility. Binary search is fundamentally a paradigm for discovering a target within a monotonic solution space.
If you can frame your problem such that a candidate answer splits the solution space into two distinct segments—where one side violates the problem's constraints and the other satisfies them—you can apply binary search. This technique is often referred to as Binary Search on the Answer or Predicate-Based Binary Search.
The Core Principle: Monotonicity
To apply binary search on an answer space, two conditions must be met:
-
A defined search range: You must know the absolute lower bound (
low) and upper bound (high) for your possible answers. - A monotonic predicate function: A boolean function $f(x)$ such that if the condition holds true for an answer $x$, it must also hold true for all answers greater than $x$ (or vice-versa depending on whether you seek a minimum or maximum).
Consider this truth array representation of a monotonic answer space:
Answer Space: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
Predicate: [ F, F, F, F, T, T, T, T, T, T ]
^ ^
Invalid Zone Valid Zone (Target is the first 'T')
Our objective is to locate the boundary transition point—the minimum value of $x$ where $f(x)$ evaluates to true.
Anatomy of the Template
Writing bug-free binary search on answer spaces requires a robust template. Avoid complex low + (high - low) / 2 index manipulation bugs by relying on a standardized left-inclusive, right-inclusive range that shrinks until convergence.
def binary_search_on_answer(low: int, high: int) -> int:
ans = -1
while low <= high:
mid = low + (high - low) // 2
if check(mid): # Predicate function
ans = mid # Record potential answer
high = mid - 1 # Try to find a smaller valid answer
else:
low = mid + 1 # Answer is too small, increase lower bound
return ans
Let us analyze how this template applies to real-world algorithmic problems.
Classic Problem 1: Koko Eating Bananas
Problem Statement
Koko loves to eat bananas. There are $n$ piles of bananas, where the $i$-th pile has piles[i] bananas. The guards have gone and will come back in $h$ hours. Koko can decide her bananas-per-hour eating speed of $k$. Each hour, she chooses some pile of bananas and eats $k$ bananas from that pile. If the pile has less than $k$ bananas, she eats all of them instead and will not eat any more bananas during this hour.
Return the minimum integer $k$ such that she can eat all the bananas within $h$ hours.
Formulating the Answer Space
-
Lower bound (
low): $1$ (She must eat at least 1 banana per hour). -
Upper bound (
high): $\max(\text{piles})$ (Eating faster than the largest pile yields no time-saving benefit). -
Predicate function
can_finish(k): Returnstrueif Koko can finish all piles in $\le h$ hours at speed $k$.
Python Implementation
import math
from typing import List
class Solution:
def minEatingSpeed(self, piles: List[int], h: int) -> int:
def can_finish(k: int) -> bool:
hours_needed = 0
for pile in piles:
# Equivalent to math.ceil(pile / k)
hours_needed += (pile + k - 1) // k
return hours_needed <= h
low, high = 1, max(piles)
result = high
while low <= high:
mid = low + (high - low) // 2
if can_finish(mid):
result = mid
high = mid - 1 # Explore if a slower speed is possible
else:
low = mid + 1 # Too slow, increase speed
return result
Classic Problem 2: Capacity To Ship Packages Within D Days
Problem Statement
A conveyor belt has packages that must be shipped from one port to another within $d$ days. The $i$-th package on the conveyor belt has a weight of weights[i]. Each day, we load the ship with packages in the order given. The weight capacity of the ship results in restrictions: we cannot load more weight than its maximum capacity on any single day.
Return the least weight capacity of the ship that will result in all the packages on the conveyor belt being shipped within $d$ days.
Formulating the Answer Space
-
Lower bound (
low): $\max(\text{weights})$ (The ship must be at least as large as the single heaviest package, otherwise that package cannot be shipped). -
Upper bound (
high): $\sum(\text{weights})$ (If the ship can hold everything at once, it takes 1 day). -
Predicate function
feasible(capacity): Returnstrueif the given capacity allows shipping all packages within $d$ days.
Java Implementation
class Solution {
public int shipWithinDays(int[] weights, int days) {
int low = 0;
int high = 0;
for (int w : weights) {
low = Math.max(low, w);
high += w;
}
int ans = high;
while (low <= high) {
int mid = low + (high - low) / 2;
if (isFeasible(weights, days, mid)) {
ans = mid;
high = mid - 1; // Try a smaller capacity
} else {
low = mid + 1; // Capacity too small, increase it
}
}
return ans;
}
private boolean isFeasible(int[] weights, int days, int capacity) {
int currentDays = 1;
int currentLoad = 0;
for (int w : weights) {
if (currentLoad + w > capacity) {
currentDays++;
currentLoad = w;
} else {
currentLoad += w;
}
}
return currentDays <= days;
}
}
Recognizing Answer Space Patterns in Interviews
When reviewing algorithmic problems, look for linguistic and structural signals indicating that binary search on the answer space applies:
| Phrase / Cue | Implication | Typical Range (low, high) |
|---|---|---|
| "Minimum possible maximum..." | Minimize a peak metric |
0 or 1 to Sum / Max Element
|
| "Maximum possible minimum..." | Maximize a bottleneck constraint |
1 to Max Element
|
| "Find the smallest speed/capacity/time..." | Boundary detection | Problem-specific logical limits |
Common Pitfalls to Avoid
-
Incorrect Search Bounds: Setting
low = 0when division by zero can occur in the predicate function (e.g., eating speed or ship capacity must be at least $1$). -
Infinite Loops: Failing to adjust
loworhighstrictly pastmid(low = mid + 1orhigh = mid - 1). -
Integer Overflow: In languages like Java or C++, computing
low + highcan overflow if values exceed $2^{31} - 1$. Always uselow + (high - low) / 2.
Conclusion
Binary search is much more than an array traversal optimization—it is a foundational problem-solving strategy for monotonic systems. By decoupling the search logic from physical memory indexes and projecting it onto an abstract domain of valid and invalid decisions, you unlock clean, $O(n \log m)$ solutions to complex resource allocation, scheduling, and optimization problems.

Top comments (0)