Editorial
Core insight
Find Peak Element · Binary Search Patterns
Core Insight for Find Peak Element
The key intuition relies on the fact that we can determine the direction of a peak by looking at the local slope. Even though the array is not sorted, the boundary conditions ( on both ends) and the distinct neighbor constraint allow us to treat the array as a series of ascending and descending slopes.
If we pick a middle element nums[mid] and compare it to its right neighbor nums[mid+1]:
- Ascending Slope (
nums[mid] < nums[mid+1]): We are climbing up. Since the array eventually ends (effectively dropping to ), there must be a peak somewhere to the right ofmid. We can safely discard the left half (includingmid). - Descending Slope (
nums[mid] > nums[mid+1]): We are going down. Since the array started at (conceptually, before index 0), there must have been a peak to the left ofmid + 1. We can safely discard the right half.
Visual Description:
Imagine the array indices plotted on the X-axis and values on the Y-axis. The algorithm selects a midpoint index. It then probes the value at mid + 1. If the line segment from mid to mid + 1 has a positive slope, the algorithm shifts its focus to the right side of the graph, knowing a local maximum exists in that direction. If the slope is negative, it focuses on the left. This process recursively narrows the window until the pointers converge on a local maximum.
Deep dive
Full breakdown
Full reasoning, pitfalls, and implementation flow
LeetCode 162: Find Peak Element Solution & Explanation
Problem Overview
TL;DR: By comparing the middle element with its immediate neighbor, we can determine the local slope of the array and eliminate the half that does not guarantee a peak, achieving logarithmic time complexity.
The LeetCode 162 problem, Find Peak Element, asks us to locate a "peak" in an array of integers. A peak is defined as an element strictly greater than its neighbors. The array is 0-indexed, and we are told to assume that elements outside the array boundaries are negative infinity (). Importantly, adjacent elements are guaranteed to be distinct. If multiple peaks exist, returning the index of any one of them is acceptable.
The critical requirement is that the solution must run in time, suggesting that a linear scan is insufficient for an optimal interview solution.
Brute Force Approach for Find Peak Element
The most straightforward way to solve this problem is to iterate through the array and check every element's neighbors.
- Iterate from index
0ton-1. - For each index
i, check ifnums[i] > nums[i-1](handling bounds) andnums[i] > nums[i+1](handling bounds). - The first index satisfying this condition is returned.
- Alternatively, finding the global maximum of the array via a linear scan also guarantees finding a peak, as the largest element is strictly greater than any existing neighbors.
Pseudocode:
for i from 0 to length - 1:
is_peak = true
if i > 0 and nums[i] < nums[i-1]:
is_peak = false
if i < length - 1 and nums[i] < nums[i+1]:
is_peak = false
if is_peak:
return iTime Complexity: . We may potentially visit every element in the array. Why it fails: While this approach produces the correct answer, the problem explicitly mandates an time complexity. In a large-scale system or strict interview environment, an solution is considered suboptimal when binary search is applicable.
Algorithm Strategy: Binary Search Patterns
We utilize the standard Binary Search template with a modification to the comparison logic.
- Initialization: Set two pointers,
leftat0andrightatn - 1. - Loop Condition: Continue while
left < right. We stop whenleftmeetsright, effectively converging on a single index. - Midpoint Calculation: Calculate
mid = left + (right - left) / 2. - Comparison: Check if
nums[mid] < nums[mid + 1].- If true, we are on an upward slope. The peak lies to the right. We update
left = mid + 1. - If false, we are on a downward slope (or
midis the peak). The peak lies to the left or is atmid. We updateright = mid.
- If true, we are on an upward slope. The peak lies to the right. We update
- Result: When the loop terminates,
left(andright) will point to a peak element. Returnleft.
Note: We compare mid with mid + 1. Because we use integer division, mid will always be less than right (as long as left < right), ensuring mid + 1 is a valid index within bounds.
Execution Flow
Consider the input: nums = [1, 2, 1, 3, 5, 6, 4]
-
Initial State:
left = 0,right = 6mid = 0 + (6 - 0) / 2 = 3nums[3]is3.
-
Iteration 1:
- Compare
nums[mid](3) vsnums[mid+1](5). 3 < 5is True. We are on an ascending slope.- The peak must be to the right.
- Update
left = mid + 1 = 4. - Range is now indices
[4, 6].
- Compare
-
Iteration 2:
left = 4,right = 6.mid = 4 + (6 - 4) / 2 = 5.nums[5]is6.- Compare
nums[mid](6) vsnums[mid+1](4). 6 < 4is False. We are on a descending slope.- The peak is at
midor to the left. - Update
right = mid = 5. - Range is now indices
[4, 5].
-
Iteration 3:
left = 4,right = 5.mid = 4 + (5 - 4) / 2 = 4.nums[4]is5.- Compare
nums[mid](5) vsnums[mid+1](6). 5 < 6is True. Ascending slope.- Update
left = mid + 1 = 5. - Range is now indices
[5, 5].
-
Termination:
left(5) is not less thanright(5). Loop ends.- Return
leftwhich is5. nums[5] = 6, which is a correct peak.
Proof of Correctness
The correctness relies on the invariant: The subarray nums[left...right] always contains at least one peak.
- Base Case: The initial range
[0, n-1]contains a peak. Since boundaries are , and the array is non-empty, there is at least one element larger than its neighbors (or the single element itself is the peak). - Inductive Step:
- If
nums[mid] < nums[mid+1], the slope is rising atmid. The subarraynums[mid+1...right]starts withnums[mid+1] > nums[mid]and ends withnums[right]having a neighbor . Therefore, a local maximum must exist in[mid+1, right]. We reduce the search space to this range. - If
nums[mid] > nums[mid+1], the slope is falling atmid. The subarraynums[left...mid]ends withnums[mid] > nums[mid+1]and starts withnums[left]having a neighbor . A local maximum must exist in[left, mid]. We reduce the search space to this range.
- If
- Termination: The search space size decreases strictly in each step. When
left == right, the search space is size 1. Due to the invariant, this single element must be a peak.
Pattern Reuse Notes
The Binary Search pattern, specifically analyzing local properties to discard halves of the array, is applicable to several other popular interview questions:
- LeetCode 33: Search in Rotated Sorted Array - Uses a similar check to determine which half of the array is sorted to decide where to search.
- LeetCode 81: Search in Rotated Sorted Array II - Handles duplicates while applying the same logic as Problem 33.
- LeetCode 153: Find Minimum in Rotated Sorted Array - Identical subpattern; compares
midagainstrightto find the inflection point (minimum) where the rotation occurred. - LeetCode 162: Find Peak Element - The problem discussed here, using local slope gradients.
Feynman coach · Before you peek at the code
Can you explain the core insight in your own words?
If you can explain it before reading code, you’re far more likely to recall it under interview pressure.
Reference Implementation
C++ Solution for LeetCode 162
1class Solution {
2public:
3 int findPeakElement(vector<int>& nums) {
4 int left = 0;
5 int right = nums.size() - 1;
6
7 // Maintain the invariant that a peak element lies within [left, right]
8 while (left < right) {
9 int mid = left + (right - left) / 2;
10
11 // Compare mid with its right neighbor
12 if (nums[mid] < nums[mid + 1]) {
13 // We are on an upward slope. The peak must be to the right.
14 // mid cannot be the peak, so we exclude it.
15 left = mid + 1;
16 } else {
17 // We are on a downward slope (or mid is the peak).
18 // The peak is either at mid or to the left.
19 right = mid;
20 }
21 }
22
23 // When left == right, we have found the peak position
24 return left;
25 }
26};Java Solution for LeetCode 162
1class Solution {
2 public int findPeakElement(int[] nums) {
3 int left = 0;
4 int right = nums.length - 1;
5
6 // Binary search loop
7 while (left < right) {
8 // Calculate mid to avoid potential overflow
9 int mid = left + (right - left) / 2;
10
11 // Check if we are in an ascending sequence
12 if (nums[mid] < nums[mid + 1]) {
13 // Peak is in the right half, exclude mid
14 left = mid + 1;
15 } else {
16 // Peak is in the left half or is mid
17 right = mid;
18 }
19 }
20
21 // left and right converge to the peak index
22 return left;
23 }
24}Python Solution for LeetCode 162
1class Solution:
2 def findPeakElement(self, nums: List[int]) -> int:
3 left, right = 0, len(nums) - 1
4
5 while left < right:
6 mid = left + (right - left) // 2
7
8 # Compare mid with its right neighbor
9 if nums[mid] < nums[mid + 1]:
10 # Ascending slope: peak is to the right
11 left = mid + 1
12 else:
13 # Descending slope: peak is to the left or at mid
14 right = mid
15
16 # When the loop ends, left == right, pointing to the peak
17 return left