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.

Think about what happens when we pick any element in the array and compare it with its neighbor:

  • If arr[mid] > arr[mid + 1], we know we're on the descending slope of the mountain. The peak must be at our current position or somewhere to the left.
  • If arr[mid] < arr[mid + 1], we're still climbing up the mountain. The peak must be somewhere to the right.

This binary decision at each point 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.

The beauty of this approach is that we don't need to find the actual maximum value - we just need to find the point where the trend changes from increasing to decreasing. Once our search range narrows down to a single element (when left == right), we've found our peak.

Learn more about Binary Search patterns.

Solution Approach

The solution implements a modified binary search algorithm to find the peak element in O(log(n)) time.

Implementation Steps:

  1. Initialize search boundaries: Set left = 1 and right = len(arr) - 2. We exclude indices 0 and n-1 because they cannot be peaks in a mountain array by definition.

  2. Binary search loop: While left < right, we continue searching:

    • Calculate the middle index: mid = (left + right) >> 1 (using bit shift for efficient division by 2)
    • Compare arr[mid] with arr[mid + 1] to determine which side of the peak we're on
  3. Decision logic:

    • If arr[mid] > arr[mid + 1]: We're on the descending part of the mountain. The peak is either at mid or to its left. Update right = mid (keeping mid in the search range since it could be the peak).
    • If arr[mid] < arr[mid + 1]: We're on the ascending part of the mountain. The peak must be to the right of mid. Update left = mid + 1 (excluding mid since it can't be the peak).
  4. Return the result: When the loop exits, left == right, pointing to the peak element. Return left.

Why this works:

  • Each iteration eliminates half of the remaining search space
  • The algorithm converges when the search range contains only one element
  • This element must be the peak since we've systematically narrowed down from both sides

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 3-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 (index of 3)
  • right = 3 (index of 4)
  • We're searching between indices 1 and 3, excluding the first and last elements

Iteration 1:

  • left = 1, right = 3
  • Calculate mid = (1 + 3) >> 1 = 2
  • Compare arr[2] with arr[3]: Is 5 > 4? Yes
  • Since arr[mid] > arr[mid + 1], we're on the descending slope
  • The peak is at index 2 or to its left
  • Update: right = mid = 2

Iteration 2:

  • left = 1, right = 2
  • Calculate mid = (1 + 2) >> 1 = 1
  • Compare arr[1] with arr[2]: Is 3 > 5? No
  • Since arr[mid] < arr[mid + 1], we're on the ascending slope
  • The peak must be to the right of index 1
  • Update: left = mid + 1 = 2

Termination:

  • Now left = 2 and right = 2
  • The loop exits since left == right
  • Return left = 2

The peak element is at index 2, which contains the value 5. Notice how we found it in just 2 comparisons instead of checking all 5 elements, demonstrating the efficiency of the binary search approach.

Solution Implementation

1class Solution:
2    def peakIndexInMountainArray(self, arr: List[int]) -> int:
3        """
4        Find the peak index in a mountain array using binary search.
5        A mountain array is an array that increases to a peak element and then decreases.
6      
7        Args:
8            arr: List[int] - The mountain array
9          
10        Returns:
11            int - The index of the peak element
12        """
13        # Initialize binary search boundaries
14        # Start from index 1 and end at len(arr) - 2 since the peak cannot be at the edges
15        left, right = 1, len(arr) - 2
16      
17        # Binary search for the peak element
18        while left < right:
19            # Calculate middle index using bit shift (equivalent to dividing by 2)
20            mid = (left + right) >> 1
21          
22            # Check if we're on the descending slope
23            if arr[mid] > arr[mid + 1]:
24                # Peak is at mid or to the left of mid
25                right = mid
26            else:
27                # We're on the ascending slope, peak is to the right of mid
28                left = mid + 1
29      
30        # When left == right, we've found the peak index
31        return left
32
1class Solution {
2    /**
3     * Finds the peak index in a mountain array using binary search.
4     * A mountain array is defined as an array that:
5     * - arr.length >= 3
6     * - There exists some i with 0 < i < arr.length - 1 such that:
7     *   - arr[0] < arr[1] < ... < arr[i - 1] < arr[i]
8     *   - arr[i] > arr[i + 1] > ... > arr[arr.length - 1]
9     * 
10     * @param arr the mountain array
11     * @return the index of the peak element
12     */
13    public int peakIndexInMountainArray(int[] arr) {
14        // Initialize binary search boundaries
15        // Start from index 1 and end at length-2 since peak cannot be at boundaries
16        int left = 1;
17        int right = arr.length - 2;
18      
19        // Binary search for the peak element
20        while (left < right) {
21            // Calculate middle index using bit shift for efficiency
22            int mid = (left + right) >> 1;
23          
24            // Check if we're on the descending slope
25            if (arr[mid] > arr[mid + 1]) {
26                // Peak is at mid or to the left of mid
27                right = mid;
28            } else {
29                // We're on the ascending slope, peak is to the right
30                left = mid + 1;
31            }
32        }
33      
34        // When left equals right, we've found the peak
35        return left;
36    }
37}
38
1class Solution {
2public:
3    int peakIndexInMountainArray(vector<int>& arr) {
4        // Initialize binary search boundaries
5        // Start from index 1 and end at size-2 since the peak cannot be at the boundaries
6        int left = 1;
7        int right = arr.size() - 2;
8      
9        // Binary search for the peak element
10        while (left < right) {
11            // Calculate middle index using bit shift (equivalent to dividing by 2)
12            int mid = (left + right) >> 1;
13          
14            // If mid element is greater than the next element,
15            // we're on the descending part of the mountain or at the peak
16            // The peak must be at mid or to the left of mid
17            if (arr[mid] > arr[mid + 1]) {
18                right = mid;
19            }
20            // If mid element is less than or equal to the next element,
21            // we're on the ascending part of the mountain
22            // The peak must be to the right of mid
23            else {
24                left = mid + 1;
25            }
26        }
27      
28        // When left == right, we've found the peak index
29        return left;
30    }
31};
32
1/**
2 * Finds the peak index in a mountain array using binary search.
3 * A mountain array is an array that increases to a peak element and then decreases.
4 * @param arr - The mountain array where arr.length >= 3
5 * @returns The index of the peak element
6 */
7function peakIndexInMountainArray(arr: number[]): number {
8    // Initialize binary search boundaries
9    // Start from index 1 and end at length-2 since peak cannot be at array boundaries
10    let left: number = 1;
11    let right: number = arr.length - 2;
12  
13    // Binary search for the peak element
14    while (left < right) {
15        // Calculate middle index using bit shift for integer division
16        const mid: number = (left + right) >> 1;
17      
18        // Check if mid is in the descending part of the mountain
19        if (arr[mid] > arr[mid + 1]) {
20            // Peak is at mid or to the left of mid
21            right = mid;
22        } else {
23            // Peak is to the right of mid
24            left = mid + 1;
25        }
26    }
27  
28    // When left equals right, we've found the peak index
29    return left;
30}
31

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. Incorrect Boundary Initialization

A frequent mistake is initializing the search boundaries as left = 0 and right = len(arr) - 1, which includes the first and last elements. This can lead to incorrect results or index out of bounds errors when checking arr[mid + 1].

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 by definition

Solution: Always initialize with left = 1 and right = len(arr) - 2 to exclude edge elements.

2. Wrong Update Logic for Search Boundaries

Another common error is incorrectly updating the search boundaries, particularly setting right = mid - 1 when arr[mid] > arr[mid + 1].

Why it's problematic:

  • When arr[mid] > arr[mid + 1], we know we're on the descending slope, but mid itself could be the peak
  • Setting right = mid - 1 would exclude the potential peak from the search range

Solution: Use right = mid (not mid - 1) when on the descending slope to keep mid in the search range.

3. Confusion with Comparison Direction

Developers sometimes mix up the comparison logic, 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 and can complicate the algorithm
  • It may lead to incorrect identification of which slope we're on

Solution: Consistently compare arr[mid] with arr[mid + 1] to determine if we're ascending or descending.

4. Handling Edge Cases Incorrectly

Not considering arrays with minimum length (n = 3) or assuming the peak could be at the boundaries.

Example of incorrect assumption:

# Wrong: Trying to handle peaks at boundaries
if arr[0] > arr[1]:
    return 0
if arr[-1] > arr[-2]:
    return len(arr) - 1

Solution: Trust the problem constraints - the peak is never at the boundaries in a valid mountain array, so no special edge case handling is needed.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

In a binary min heap, the maximum element can be found in:


Recommended Readings

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

Load More