Data Structures · Binary Search · A Field Guide

Find a peak
element

There's no target and the array isn't sorted. But the local slope always points toward higher ground — and uphill has to end in a summit somewhere.

1231
01 · Strip it down

Return any element bigger than both neighbors, in O(log n)

A peak is strictly greater than its left and right neighbors. In [1,2,3,1], index 2 (value 3) is a peak. You may return any peak, and the ends count too — imagine nums[-1] = nums[n] = −∞, so a rising or falling edge still qualifies. That guarantees every array has at least one peak. A linear scan finds one in O(n), but the required O(log n) forces binary search — on an unsorted array.


02 · The slope decision

Compare mid to its right neighbor

There's no target to compare against, so inspect the local shape instead: nums[mid] vs nums[mid+1]. That single comparison reveals a slope, and the slope proves which side must contain a peak.

Uphill — nums[mid] < nums[mid+1]
go right: left = mid + 1
mid can't be a peak; a summit lies to the right.
Downhill — nums[mid] > nums[mid+1]
go left, keep mid: right = mid
mid itself might be the peak, so don't discard it.
Why uphill must end in a peak Climb right from a rising point: either the values eventually turn down — that turn is a peak — or they rise all the way to the last index, which beats the imaginary −∞ past the edge. Either way, the right half holds a peak.

Climbing in fog

You can't see the whole range, only whether the next step is up or down. Always stepping uphill guarantees you reach a summit — maybe not the tallest, but a real one.

Follow the rising water

Toss a ball; it rolls to a low point. Invert that — walk toward the rising side and you're funneled to a high point. The slope does the navigating.

Keep only one valid answer

Binary search may throw away a half that contains a peak — that's fine. It only has to keep at least one, not all of them.


04 · Watch it climb

The array as terrain

Set an array, then step. Each step marks mid (with its right neighbor dashed), reads the slope, and discards a half — the live range stays full-color, discarded bars fade. When one bar remains it locks in as a peak (teal).

Peak finder

Try 1 2 3 4 5 (all uphill → last index) or 5 4 3 2 1 (all downhill → index 0). Multiple peaks? Any one is a valid answer.


05 · Trace table

Finding a peak in [1,2,1,3,5,6,4]

left mid right nums[mid] nums[mid+1] Slope Action
0 3 6 3 5 up left = 4
4 5 6 6 4 down right = 5
4 4 5 5 6 up left = 5

Loop ends with left = right = 5 → peak at index 5 (value 6). This array has two peaks (indices 1 and 5); the search converged on one, which is all the problem asks.


06 · The code

Four lines that carry a proof

int findPeakElement(int[] nums) {
    int lo = 0, hi = nums.length - 1;
    while (lo < hi) {
        int mid = lo + (hi - lo) / 2;
        if (nums[mid] < nums[mid + 1]) lo = mid + 1;  // uphill → right
        else hi = mid;                                // downhill → keep mid, left
    }
    return lo;
}
Loop is lo < hi
stops at one candidate
When lo == hi, a single index remains — and the invariant says it's a peak.
mid + 1 is always safe
mid < hi ≤ n−1
Since lo < hi, mid never equals hi, so mid+1 stays in bounds — no edge checks.

O(log n) time, O(1) space. The interval shrinks every step (left jumps past mid, or right drops to mid), so it always terminates.


07 · Why it's correct

The interval always holds a peak

The invariant [left, right] always contains at least one peak. It's true at the start (the whole array has one), and each slope decision keeps a half that provably still contains one. When the range narrows to a single index, that index must be a peak.

This leans on a liberating idea: binary search doesn't need to preserve every valid answer, only at least one. An array can have several peaks; discarding a half that happens to hold one is fine as long as the kept half is guaranteed to hold another.


08 · Traps to avoid

Four ways it slips

right = mid − 1

On a downhill, mid itself can be the peak ([1,3,2]). Dropping it discards the answer — use right = mid.

Checking both neighbors

Comparing mid−1 and mid+1 works but needs edge guards for index 0 and n−1. The one-sided slope test needs none.

Hunting the maximum

The global max is a peak, but finding it is O(n) and sorting is O(n log n). You only need a local peak — cheaper.

Assuming a mountain shape

The array can zigzag (1 3 2 5 4 8 6) with many peaks. The slope proof still guarantees one on the kept side.


09 · The pattern

Binary search on a local optimum

This is the strongest break from "binary search = sorted array + target." Here there's no target and no global order — just a local slope that reliably points toward a valid answer. That's enough to halve the space. The same directional idea powers mountain-array peaks, bitonic arrays, and unimodal-function optimization.

Rotated search
one half is sorted
recover local order to discard a half
Find peak
slope points uphill
follow the side guaranteed to hold a peak

The unifying definition: binary search is any method that repeatedly proves one half can't be the answer and throws it away — sortedness is just the most familiar way to earn that proof.


10 · One month from now

Reconstruct it, don't memorize it

  1. I need any peak, in O(log n), on an unsorted array.
  2. No target — so compare mid with its right neighbor for a slope.
  3. Uphill (mid < mid+1): a peak must lie right → left = mid + 1.
  4. Downhill (mid > mid+1): mid or something left is a peak → right = mid.
  5. The range always holds a peak; when one index remains, it's the answer.
The transferable insight Binary search can locate a local optimum whenever a local slope gives a reliable direction in which at least one valid answer must exist. Trade the narrow "sorted + target" model for the real one: repeatedly prove a half can be discarded.