Facebook Pixel

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 n is 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 at mid or to its left
  • If arr[mid] < arr[mid + 1], we're on the ascending slope, so the peak is to the right of mid

The search continues until left and right converge to the peak element's index.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 is true), 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 is false), 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
19
1class 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}
24
1class 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};
25
1/**
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}
22

Solution 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:

  1. Initialize: Set left = 1, right = len(arr) - 2, and first_true_index = -1. We exclude boundary indices since they cannot be peaks.

  2. Define the feasible condition: arr[mid] > arr[mid + 1] means we're at the peak or on the descending slope.

  3. Binary search loop (while left <= right):

    • Calculate mid = (left + right) // 2
    • If feasible (arr[mid] > arr[mid + 1]): save first_true_index = mid, then search left (right = mid - 1)
    • If not feasible: search right (left = mid + 1)
  4. Return: first_true_index contains 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 smaller true
  • When we find a false, we search right
  • The final first_true_index is 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 Evaluator

Example 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
Iterationleftrightmidarr[mid] > arr[mid+1]?Action
11325 > 4? Yesfirst_true_index = 2, right = 1
21113 > 5? Noleft = 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 mid equals len(arr) - 1, then arr[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.

Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

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
12
1public 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}
16
1function 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}
16

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More