skip to content

questions

4

Why can a binary-search-style method find a local maximum in an unsorted array?

level: juniorimportance: must knowfreq 60%

answer

  1. sorted is sufficient, not necessary
  2. what does halving actually require?
  3. a midpoint test that kills half safely
  4. compare a sample to its right neighbour
  5. climb uphill; a finite array must end

basics

~20 s

Binary search needs a rule that safely discards half the input, not sorted data. Comparing a sample with its right neighbour shows an uphill direction, and the uphill half always still contains a local maximum, so halving stays correct.

solid answer

~40 s

Sortedness is sufficient for binary search, not necessary. What the technique actually needs is a cheap test at the midpoint that proves one half can be thrown away without losing an answer. For a local maximum over, say, a run of drone-altitude samples, that test is a slope comparison: if `a[mid] < a[mid+1]` the readings rise to the right, so the right side must climb to a local maximum before it either turns over or runs out of array; otherwise the left side, including `mid` itself, must contain one. Either way half the range dies per step, giving O(log n) time and O(1) extra space. The catch is what you are promised: *a* local maximum, not the largest value in the array, and a different probe path may legitimately land on a different peak.

go deeper

for a junior

Be ready to state what a local maximum is, including the convention that the array ends count, and to say out loud that binary search needs a safe halving rule rather than sorted data.

for a middle

Explain the slope comparison and walk the argument for why the kept half still contains a peak, then give the O(log n) time and O(1) space costs without being prompted.

for a senior

Show that you know what the contract actually promises: one arbitrary peak, not the maximum and not a stable index, and say when a plain linear scan is the better engineering call.

for a principal

Own the framing question — is 'any turning point' what the consumer needs at all? Choosing the weaker, cheaper guarantee is only defensible when someone has checked the requirement against it.

## What "peak" means here A local maximum — a peak — in a one-dimensional series is an index `i` whose value is at least as large as its immediate neighbours: `a[i] >= a[i-1]` and `a[i] >= a[i+1]`. The two ends need a convention: treat the positions just outside the array as holding negative infinity, so index `0` counts as a peak whenever `a[0] >= a[1]`, and the last index counts whenever it is at least as large as the one before it. Without that convention a strictly rising series of altitude samples would contain no peak at all and the search would have nothing to return. Most interview framings also assume adjacent samples are never equal, which makes the peak strict; flat runs are discussed below. ## Sortedness is sufficient, not necessary The usual mental model — "binary search requires sorted input" — states a special case as if it were the rule. What halving really requires is a **decision procedure at the probe point that provably preserves an answer in the half you keep**. In classic search-for-a-value, sortedness is what supplies that procedure: if the target is greater than the midpoint value, every element to the left is also smaller than the target, so the left side cannot hold it. Sortedness is one way to buy the guarantee. It is not the only way. Peak finding buys the same guarantee from a completely different fact: an unsorted series is still a sequence of rises and falls, and a rise cannot continue forever inside a finite array. ## The halving argument Probe the midpoint and compare it with its right neighbour. - If `a[mid] < a[mid+1]`, the readings are rising at `mid`. Walk right from `mid+1`. Either the values keep rising until the last index — which is then a peak under the end convention — or at some point they stop rising, and the last index before that drop is a peak. Either way the sub-range strictly to the right of `mid` contains at least one local maximum, so the left half can be discarded. - If `a[mid] >= a[mid+1]`, the same argument runs leftward: walk left from `mid` and you either keep rising to index `0`, which is then a peak, or you turn over at one. So the range from the low bound through `mid` contains a peak, and the right half can go. Notice what the argument does **not** claim: it never says the discarded half is peak-free. It usually is not. The claim is only that the surviving half still contains one, which is all a search for *any* peak needs. ## Cost Each step does O(1) comparisons and halves the range, so the recurrence is T(n) = T(n/2) + O(1), giving O(log n) time. Written iteratively it uses O(1) auxiliary space; written recursively the call stack is O(log n), and recursion depth counts as space. Contrast this with the obvious alternative — scan every sample and keep the largest — which is O(n) but answers a strictly stronger question. ## What you are promised, and what you are not The result is a valid local maximum. It is **not**: - the global maximum — a taller peak elsewhere is entirely possible and no comparison ever ruled it out; - deterministic across implementations — a variant that probes `a[mid]` against its *left* neighbour, or rounds the midpoint the other way, will often return a different index on the same input, and both are correct; - an enumeration — you get one peak, never the list of them. Listing all local maxima requires touching every element, so it is inherently O(n). Any requirement phrased as "the highest reading" or "all the turning points" is therefore the wrong fit for this technique, and reaching for it anyway is the single most common misuse. ## Boundaries and flat runs Two details cause almost every bug. First, the probe touches `mid+1`, so the loop bounds must guarantee that index exists; a loop condition of `lo < hi` with a floored midpoint does, because it forces `mid < hi`. Second, equal adjacent values break the reasoning's grip: if `a[mid] == a[mid+1]` the local probe carries no information about which side holds a *strict* peak, so either you accept the "greater than or equal to both neighbours" definition — under which the halving loop still returns a valid answer — or you may be forced into a linear scan. ## When a linear scan wins anyway For a few hundred samples the constant factors and the extra reviewer-minutes make the O(n) scan the better engineering choice, and it answers more questions (global maximum, peak count, all peaks) for the same one pass. The logarithmic method earns its keep when the series is large, random access is cheap, and "any turning point" is genuinely the question being asked — or when probing a sample is itself expensive, since it makes O(log n) probes instead of n.

  • Does this return the largest value in the array?
    No. It returns some local maximum. No comparison in the run ever examined most of the array, so a taller peak elsewhere is entirely possible. Getting the global maximum requires looking at every element, which is O(n) — a strictly more expensive and strictly stronger question.
  • What happens when the series has several local maxima?
    Any one of them is a correct answer, and which one you land on is decided by the probe path — midpoint rounding, and whether you compare against the left or right neighbour. Two correct implementations can return different indices on the same input, which matters if a test or a downstream consumer pins one index.
  • What if the samples rise monotonically from start to finish?
    The last index is the answer, under the convention that the positions outside the array hold negative infinity. Without that convention such an input has no peak at all and the contract is ill-defined, so the convention is not a technicality — it is what makes the function total.

Hiking in fog with no map: you cannot see the tallest summit, but if the ground rises ahead of you, stepping that way must eventually bring you to a point where it stops rising.

saying these in an interview costs you the question

  • Claims binary search always requires sorted input
  • Says the method returns the global maximum
  • Thinks the discarded half provably contains no peak
  • Calls the search O(n) because the data is unsorted
  • Cannot say what happens on a strictly rising series

context

open as a page

In peak finding, when a[mid] < a[mid+1], why is discarding the entire left half safe?

level: middleimportance: must knowfreq 50%

basics

~20 s

The loop maintains an invariant, not an order: the surviving range always contains a peak. When a[mid] < a[mid+1] the readings rise, and a rising path inside a finite array must reach a local maximum before it runs out.

open as a page

When should a telemetry API promise any local peak in O(log n) instead of the global maximum?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Promise a local peak only when consumers genuinely need a point where the climb stops and the input is guaranteed single-humped, so the two answers coincide. Promise the global maximum whenever results are compared, alerted on or reported.

open as a page