852. Peak Index in a Mountain Array
Problem Description
You are given an integer array arr that represents a mountain array. A mountain array has the following properties:
- The array length
nis at least 3 - The values strictly increase from the start until reaching a peak element
- After the peak, the values strictly decrease until the end
- There is exactly one peak element in the array
Your task is to find and return the index of the peak element (the highest value in the array).
For example, in the array [1, 3, 5, 4, 2], the peak element is 5 at index 2.
Constraint: You must solve this problem in O(log(n)) time complexity, which means you cannot simply iterate through the array to find the maximum. Instead, you need to use a more efficient approach like binary search.
The solution uses binary search to efficiently locate the peak. It starts with left = 1 and right = len(arr) - 2 (excluding the first and last elements since they cannot be peaks). At each step, it calculates the middle index and compares arr[mid] with arr[mid + 1]:
- If
arr[mid] > arr[mid + 1], we're on the descending slope, so the peak is atmidor to its left - If
arr[mid] < arr[mid + 1], we're on the ascending slope, so the peak is to the right ofmid
The search continues until left and right converge to the peak element's index.
Intuition
The key insight is recognizing that a mountain array has a special property: it's divided into two parts - an ascending part and a descending part, with a single peak between them. This structure naturally lends itself to binary search.
We can frame this as a "find first true" problem. For each index i, we ask: "Is arr[i] > arr[i + 1]?" This creates a boolean pattern:
- On the ascending slope:
false, false, false, ... - At and after the peak:
true, true, true, ...
The array looks like: [false, false, ..., true, true, ...] where the first true position is the peak index.
Think about what happens when we pick any element and compare it with its neighbor:
- If
arr[mid] > arr[mid + 1](condition istrue), we're at or past the peak. The peak must be at our current position or somewhere to the left. - If
arr[mid] < arr[mid + 1](condition isfalse), we're still climbing up the mountain. The peak must be somewhere to the right.
This binary decision allows us to eliminate half of the search space with each comparison, achieving the required O(log(n)) time complexity.
Why can we start with left = 1 and right = len(arr) - 2? Because the first and last elements can never be peaks in a mountain array - the first element only has a neighbor to its right that must be greater, and the last element only has a neighbor to its left that must be greater.
Learn more about Binary Search patterns.
Solution Implementation
1class Solution:
2 def peakIndexInMountainArray(self, arr: List[int]) -> int:
3 """
4 Find the peak index using binary search template.
5 Feasible condition: arr[mid] > arr[mid + 1] (at or past peak)
6 """
7 left, right = 1, len(arr) - 2
8 first_true_index = -1
9
10 while left <= right:
11 mid = (left + right) // 2
12 if arr[mid] > arr[mid + 1]:
13 first_true_index = mid
14 right = mid - 1
15 else:
16 left = mid + 1
17
18 return first_true_index
191class Solution {
2 /**
3 * Find the peak index using binary search template.
4 * Feasible condition: arr[mid] > arr[mid + 1] (at or past peak)
5 */
6 public int peakIndexInMountainArray(int[] arr) {
7 int left = 1;
8 int right = arr.length - 2;
9 int firstTrueIndex = -1;
10
11 while (left <= right) {
12 int mid = left + (right - left) / 2;
13 if (arr[mid] > arr[mid + 1]) {
14 firstTrueIndex = mid;
15 right = mid - 1;
16 } else {
17 left = mid + 1;
18 }
19 }
20
21 return firstTrueIndex;
22 }
23}
241class Solution {
2public:
3 /**
4 * Find the peak index using binary search template.
5 * Feasible condition: arr[mid] > arr[mid + 1] (at or past peak)
6 */
7 int peakIndexInMountainArray(vector<int>& arr) {
8 int left = 1;
9 int right = arr.size() - 2;
10 int firstTrueIndex = -1;
11
12 while (left <= right) {
13 int mid = left + (right - left) / 2;
14 if (arr[mid] > arr[mid + 1]) {
15 firstTrueIndex = mid;
16 right = mid - 1;
17 } else {
18 left = mid + 1;
19 }
20 }
21
22 return firstTrueIndex;
23 }
24};
251/**
2 * Find the peak index using binary search template.
3 * Feasible condition: arr[mid] > arr[mid + 1] (at or past peak)
4 */
5function peakIndexInMountainArray(arr: number[]): number {
6 let left = 1;
7 let right = arr.length - 2;
8 let firstTrueIndex = -1;
9
10 while (left <= right) {
11 const mid = Math.floor((left + right) / 2);
12 if (arr[mid] > arr[mid + 1]) {
13 firstTrueIndex = mid;
14 right = mid - 1;
15 } else {
16 left = mid + 1;
17 }
18 }
19
20 return firstTrueIndex;
21}
22Solution Approach
The solution uses the standard binary search template to find the first index where arr[i] > arr[i + 1].
Implementation using the standard template:
left, right = 1, len(arr) - 2
first_true_index = -1
while left <= right:
mid = (left + right) // 2
if arr[mid] > arr[mid + 1]: # feasible condition: at or past peak
first_true_index = mid
right = mid - 1
else:
left = mid + 1
return first_true_index
Step-by-step breakdown:
-
Initialize: Set
left = 1,right = len(arr) - 2, andfirst_true_index = -1. We exclude boundary indices since they cannot be peaks. -
Define the feasible condition:
arr[mid] > arr[mid + 1]means we're at the peak or on the descending slope. -
Binary search loop (
while left <= right):- Calculate
mid = (left + right) // 2 - If feasible (
arr[mid] > arr[mid + 1]): savefirst_true_index = mid, then search left (right = mid - 1) - If not feasible: search right (
left = mid + 1)
- Calculate
-
Return:
first_true_indexcontains the peak index.
Why this works:
- The feasible condition creates a
[false, false, ..., true, true, ...]pattern - When we find a
true, we save it and search left for an even smallertrue - When we find a
false, we search right - The final
first_true_indexis the smallest index where the condition holds - the peak
Time Complexity: O(log(n)) - we divide the search space in half with each iteration
Space Complexity: O(1) - only using a constant amount of extra space for variables
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through finding the peak in the mountain array [1, 3, 5, 4, 2].
Initial Setup:
- Array:
[1, 3, 5, 4, 2](length = 5) left = 1,right = 3,first_true_index = -1- Feasible condition:
arr[mid] > arr[mid + 1]
Iteration 1:
left = 1,right = 3- Calculate
mid = (1 + 3) // 2 = 2 - Check feasible:
arr[2] > arr[3]? Is 5 > 4? Yes (true) - Save
first_true_index = 2, search left:right = mid - 1 = 1
Iteration 2:
left = 1,right = 1- Calculate
mid = (1 + 1) // 2 = 1 - Check feasible:
arr[1] > arr[2]? Is 3 > 5? No (false) - Search right:
left = mid + 1 = 2
Termination:
- Now
left = 2 > right = 1 - The loop exits since
left > right - Return
first_true_index = 2
| Iteration | left | right | mid | arr[mid] > arr[mid+1]? | Action |
|---|---|---|---|---|---|
| 1 | 1 | 3 | 2 | 5 > 4? Yes | first_true_index = 2, right = 1 |
| 2 | 1 | 1 | 1 | 3 > 5? No | left = 2 |
The peak element is at index 2, which contains the value 5. We found it in just 2 comparisons instead of checking all 5 elements.
Time and Space Complexity
Time Complexity: O(log n), where n is the length of the input array. The algorithm uses binary search to find the peak element. In each iteration, the search space is reduced by half. Starting with n-2 elements (excluding the first and last elements), the algorithm performs at most log₂(n-2) iterations, which simplifies to O(log n).
Space Complexity: O(1). The algorithm only uses a constant amount of extra space for the variables left, right, and mid, regardless of the input size. No additional data structures are created that scale with the input size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Using the Wrong Binary Search Template
Pitfall: Using while left < right with right = mid instead of the standard template.
Incorrect:
while left < right: mid = (left + right) // 2 if arr[mid] > arr[mid + 1]: right = mid else: left = mid + 1 return left
Correct (using standard template):
first_true_index = -1 while left <= right: mid = (left + right) // 2 if arr[mid] > arr[mid + 1]: first_true_index = mid right = mid - 1 else: left = mid + 1 return first_true_index
2. Incorrect Boundary Initialization
Pitfall: Initializing left = 0 and right = len(arr) - 1, which includes boundary elements.
Why it's problematic:
- If
midequalslen(arr) - 1, thenarr[mid + 1]causes an IndexError - The first and last elements cannot be peaks in a valid mountain array
Solution: Always initialize with left = 1 and right = len(arr) - 2.
3. Confusion with Comparison Direction
Pitfall: Checking arr[mid] < arr[mid - 1] instead of arr[mid] > arr[mid + 1].
Why it's problematic:
- Comparing with
arr[mid - 1]requires different boundary logic - It may lead to incorrect identification of which slope we're on
Solution: Consistently compare arr[mid] with arr[mid + 1] for the feasible condition.
4. Handling Edge Cases Incorrectly
Pitfall: Adding special cases for boundary elements.
Example of incorrect assumption:
# Wrong: Trying to handle peaks at boundaries if arr[0] > arr[1]: return 0
Solution: Trust the problem constraints - the peak is never at the boundaries in a valid mountain array.
Given a sorted array of integers and an integer called target, find the element that
equals to the target and return its index. Select the correct code that fills the
___ in the given code snippet.
1def binary_search(arr, target):
2 left, right = 0, len(arr) - 1
3 while left ___ right:
4 mid = (left + right) // 2
5 if arr[mid] == target:
6 return mid
7 if arr[mid] < target:
8 ___ = mid + 1
9 else:
10 ___ = mid - 1
11 return -1
121public static int binarySearch(int[] arr, int target) {
2 int left = 0;
3 int right = arr.length - 1;
4
5 while (left ___ right) {
6 int mid = left + (right - left) / 2;
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
161function binarySearch(arr, target) {
2 let left = 0;
3 let right = arr.length - 1;
4
5 while (left ___ right) {
6 let mid = left + Math.trunc((right - left) / 2);
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Want a Structured Path to Master System Design Too? Don’t Miss This!