Problem
Given an array that first strictly increases then strictly decreases (a "mountain"), return the index of the peak element โ the point where the array switches from going up to going down.
- Input:
arr = [0, 2, 1, 0] - Output:
1 - Explanation:
arr[1] = 2is the peak โ it's greater than both its neighbors.
A counter-example: [0, 1, 2, 3] is not a mountain array โ it never decreases, so no peak exists (the problem guarantees input is always a valid mountain).
Intuition
The problem asks for the index where the array transitions from ascending to descending. At any midpoint, comparing the midpoint to its right neighbor tells you exactly which half the peak is in: if the array is still climbing, the peak is to the right; if it has already started falling, the peak is at or to the left. This lets you eliminate half the array at every step, turning a linear scan into a binary search.
Approach 1 โ Linear Scan
Scan left to right. The first position where the array stops climbing โ where the current element is greater than the next โ is the peak.
- Iterate from index 1 to the second-to-last element.
- At each index, check if
arr[current] > arr[current + 1]. - If so, return
currentโ this is the peak.
1def peakIndexInMountainArray(arr: list[int]) -> int:
2 for i in range(1, len(arr) - 1):
3 if arr[i] > arr[i + 1]: # array has started descending, peak found
4 return i
5 return -1 # unreachable given valid mountain inputTime: O(n) โ in the worst case the peak is at the last valid position.
Space: O(1) โ only two variables used.
Approach 2 โ Binary Search
At any midpoint mid, compare arr[mid] to arr[mid + 1]:
- If
arr[mid] < arr[mid + 1], the array is still ascending, so the peak is to the right โ movelefttomid + 1. - Otherwise, the array is descending or at the peak โ move
righttomid(the peak could be exactly atmid).
- Initialize
left = 0,right = len(arr) - 1. - While
left < right:- Compute
mid = (left + right) // 2. - If
arr[mid] < arr[mid + 1], setleft = mid + 1. - Else set
right = mid.
- Compute
- Return
leftโ the search converges to the peak index.
1def peakIndexInMountainArray(arr: list[int]) -> int:
2 left, right = 0, len(arr) - 1
3 while left < right:
4 mid = (left + right) // 2
5 if arr[mid] < arr[mid + 1]: # still ascending, peak is to the right
6 left = mid + 1
7 else: # descending or at peak, peak is here or left
8 right = mid
9 return leftTime: O(log n) โ search space halves at every step.
Space: O(1) โ only three pointer variables used.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Linear Scan | O(n) | O(1) | Small arrays or when clarity matters more than performance |
| Binary Search | O(log n) | O(1) | Default choice; always preferred for this problem |
Common Mistakes
- Using
while left <= rightwitharr[mid + 1]โ whenleft == right == mid,mid + 1is out of bounds. The conditionwhile left < rightguaranteesmid < right, soarr[mid + 1]is always a valid index. - Setting
right = mid - 1in the descending branch โ the peak could be exactly atmid(sincearr[mid] >= arr[mid + 1]includes equality at the peak). Usingmid - 1skips the correct answer. - Returning the last
midinstead ofleftโ the loop exits whenleft == right, which is the peak. The last computedmidvalue is typically one step behind; always returnleft(orright). - Not realizing the
mid + 1comparison is safe in the binary search โ becausemid = (left + right) // 2always producesmid < rightwhenleft < right, somid + 1 <= right <= len(arr) - 1.
Related Problems
find-peak-elementโ same binary search comparison but the array isn't guaranteed to be a clean mountain, requiring a slightly more careful conditionbinary-searchโ the foundational pattern this solution builds onsearch-in-rotated-sorted-arrayโ binary search where the sorted-half condition determines which pointer movessingle-element-in-a-sorted-arrayโ binary search with a parity-based comparison instead of a slope-based onekoko-eating-bananasโ binary search on the answer space rather than directly on indices