Find Peak Element medium
Description
A peak element is an element that is strictly greater than its neighbors. Given a 0-indexed integer array nums, find a peak element and return its index. You may assume that nums[-1] = nums[n] = -∞ (i.e., elements at the boundaries always count as “greater than the imaginary out-of-bounds neighbor”).
You must write an algorithm that runs in O(log n) time.
Examples
> Case 1:
nums = [1, 2, 3, 1]
Output: 2 # nums[2] = 3 is a peak
> Case 2:
nums = [1, 2, 1, 3, 5, 6, 4]
Output: 1 or 5 # both nums[1] = 2 AND nums[5] = 6 are peaksConstraints
1 <= nums.length <= 1000-2^31 <= nums[i] <= 2^31 - 1nums[i] != nums[i + 1]for all validi.
Why binary search works on an unsorted array
The array isn’t sorted — yet binary search still works. The trick: the “slope” gives us a direction.
Look at nums[mid] and nums[mid + 1]:
- If
nums[mid] < nums[mid + 1]— the array is rising at this point. A peak must exist to the right (we’ll either keep rising forever — but the boundary acts as-∞so we eventually peak — or hit a peak somewhere). - If
nums[mid] > nums[mid + 1]— the array is falling. A peak must exist to the left or at mid itself (we’re past a peak now).
Because the boundaries are treated as -∞, a peak is guaranteed to exist. Binary search converges on it.
Code
The predicate is “we’re past the peak” — true when nums[mid] > nums[mid + 1]. That predicate IS monotonic once you spot it: before the peak it’s false, after the peak it stays true. Binary search finds the boundary, which is exactly the peak.
This is a beautiful example of binary search working on an unsorted array — what matters isn’t sortedness but monotonicity of a derived predicate.
Why we don’t check nums[mid - 1] too
You might think we need to compare BOTH sides — nums[mid - 1] < nums[mid] > nums[mid + 1]. We don’t, because:
- The binary search range
[lo, hi]always contains at least one peak. - The slope comparison narrows that range correctly.
- When
lo == hi, the converged-on index IS a peak.
Trust the invariant — one comparison per iteration is enough.
Trace on [1, 2, 1, 3, 5, 6, 4]
Step 1: lo=0, hi=6, mid=3, nums[3]=3, nums[4]=5. 3 < 5 → lo = 4.
Step 2: lo=4, hi=6, mid=5, nums[5]=6, nums[6]=4. 6 > 4 → hi = 5.
Step 3: lo=4, hi=5, mid=4, nums[4]=5, nums[5]=6. 5 < 6 → lo = 5.
End: lo == hi == 5. Return 5.
(The other valid answer was 1; we converged on the one in the right half.)The “any valid answer” subtlety
This problem accepts any peak as a valid answer. That’s why the convergence works regardless of which side we go — the loop is guaranteed to land on a peak, not necessarily the first peak.
If the problem asked for “the global maximum” (a different question), this binary search approach wouldn’t work — you’d need to scan all elements in O(n).
Iterative thought: why this is O(log n)
Each iteration halves the search space. Standard binary-search reasoning gives O(log n). The fact that the array isn’t sorted doesn’t change this — the slope predicate is what matters.
Related problems
| Problem | What changes |
|---|---|
| Peak Index in a Mountain Array | Mountain shape guaranteed (rising then falling) |
| Find a Local Minimum | Same logic with < flipped |
| Bitonic Search | Find peak first, then binary-search each half |
| Ternary Search for unimodal max | Same idea, but continuous (real-valued) |
The pattern transfers anywhere you have a “rises then falls” structure.
Analysis
- Time: O(log n) — binary search.
- Space: O(1).