The Peak of a Mountain Array

Prereq: Vanilla Binary Search and Finding the Boundary with Binary Search

A mountain array is defined as an array that

  • has at least 3 elements
  • has an element with the largest value called "peak", with index k. The array elements strictly increase from the first element to A[k], and then strictly decreases from A[k + 1] to the last element of the array. Thus creating a "mountain" of numbers.

That is, given A[0]<...<A[k-1]<A[k]>A[k+1]>...>A[n-1], we need to find the index k. Note that the peak element is neither the first nor the last element of the array.

Find the index of the peak element. Assume there is only one peak element.

Input: 0 1 2 3 2 1 0

Output: 3

Explanation: the largest element is 3 and its index is 3.

Try it yourself

Explanation

The array strictly increases until the peak element and then strictly decreases. The monotonicity is a strong sign that we can use binary search to find the peak element.

To use binary search though, we need the entire search range to be strictly increasing or decreasing. We need to find the feasible function that returns false for elements up until the peak and true from the peak to the end.

We already know the array strictly decreases from the peak element to the last element. So we can try to use a feasible function of arr[i]> arr[i+1] to return true for elements from the peak to the last element. Once we do that, we realize that also returns false from the first element to the peak element. We got our feasible function.

A minor edge case is for the last element as it has no next element. We can pad the array with an imaginary node of negative infinity. In the implementation, we don't actually need to pad the array as that would incur O(n) extra cost. We can just check if i+1 is out of bounds and return true if it is since this implies arr[i] is the last element.

Now the problem is reduced to finding the first true element in a boolean array. And we already know how to do this from Find the First True in a Sorted Boolean Array module.

Time Complexity: O(log(n))

Space Complexity: O(1)

1from typing import List
2
3def peak_of_mountain_array(arr: List[int]) -> int:
4    alen = len(arr)
5    left, right = 0, alen - 1
6    boundary_index = -1
7
8    while left <= right:
9        mid = (left + right) // 2
10        if mid == alen - 1 or arr[mid] > arr[mid + 1]:
11            boundary_index = mid
12            right = mid - 1
13        else:
14            left = mid + 1
15
16    return boundary_index
17
18if __name__ == '__main__':
19    arr = [int(x) for x in input().split()]
20    res = peak_of_mountain_array(arr)
21    print(res)
22
1import java.util.Arrays;
2import java.util.List;
3import java.util.Scanner;
4import java.util.stream.Collectors;
5
6class Solution {
7    public static int peakOfMountainArray(List<Integer> arr) {
8        int alen = arr.size();
9        int left = 0;
10        int right = alen - 1;
11        int boundaryIndex = -1;
12        while (left <= right) {
13            int mid = left + (right - left) / 2;
14            if (mid == alen-1 || arr.get(mid) > arr.get(mid + 1)) {
15                boundaryIndex = mid;
16                right = mid - 1;
17            } else {
18                left = mid + 1;
19            }
20        }
21        return boundaryIndex;
22    }
23
24    public static List<String> splitWords(String s) {
25        return s.isEmpty() ? List.of() : Arrays.asList(s.split(" "));
26    }
27
28    public static void main(String[] args) {
29        Scanner scanner = new Scanner(System.in);
30        List<Integer> arr = splitWords(scanner.nextLine()).stream().map(Integer::parseInt).collect(Collectors.toList());
31        scanner.close();
32        int res = peakOfMountainArray(arr);
33        System.out.println(res);
34    }
35}
36
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5class Solution
6{
7    public static int PeakOfMountainArray(List<int> arr)
8    {
9        int alen = arr.Count;
10        int left = 0;
11        int right = alen - 1;
12        int boundaryIndex = -1;
13        while (left <= right)
14        {
15            int mid = left + (right - left) / 2;
16            if (mid == alen -1 || arr[mid] > arr[mid + 1])
17            {
18                boundaryIndex = mid;
19                right = mid - 1;
20            }
21            else
22            {
23                left = mid + 1;
24            }
25        }
26        return boundaryIndex;
27    }
28
29    public static List<string> SplitWords(string s)
30    {
31      return string.IsNullOrEmpty(s) ? new List<string>() : s.Trim().Split(' ').ToList();
32    }
33
34    public static void Main()
35    {
36        List<int> arr = SplitWords(Console.ReadLine()).Select(int.Parse).ToList();
37        int res = PeakOfMountainArray(arr);
38        Console.WriteLine(res);
39    }
40}
41
1function peakOfMountainArray(arr) {
2    let alen = arr.length;
3    let left = 0;
4    let right = arr.length - 1;
5    let boundary_index = -1;
6    while (left <= right) {
7        let mid = left + Math.floor((right - left) / 2);
8        if (mid == alen - 1 || arr[mid] > arr[mid + 1]) {
9            boundary_index = mid;
10            right = mid - 1;
11        } else {
12            left = mid + 1;
13        }
14    }
15    return boundary_index;
16}
17
18function splitWords(s) {
19    return s == "" ? [] : s.split(' ');
20}
21
22function* main() {
23    const arr = splitWords(yield).map((v) => parseInt(v));
24    const res = peakOfMountainArray(arr);
25    console.log(res);
26}
27
28class EOFError extends Error {}
29{
30    const gen = main();
31    const next = (line) => gen.next(line).done && process.exit();
32    let buf = '';
33    next();
34    process.stdin.setEncoding('utf8');
35    process.stdin.on('data', (data) => {
36        const lines = (buf + data).split('\n');
37        buf = lines.pop();
38        lines.forEach(next);
39    });
40    process.stdin.on('end', () => {
41        buf && next(buf);
42        gen.throw(new EOFError());
43    });
44}
45
1#include <algorithm> // copy
2#include <iostream> // boolalpha, cin, cout
3#include <iterator> // back_inserter, istream_iterator
4#include <sstream> // istringstream
5#include <string> // getline, string
6#include <vector> // vector
7
8int peak_of_mountain_array(std::vector<int> arr) {
9    int left = 0, right = arr.size() - 1, boundary_index = -1, alen = arr.size();
10    while (left <= right) {
11        int mid = left + (right - left) / 2;
12        if (mid == alen-1 || arr[mid] > arr[mid + 1]) {
13            boundary_index = mid;
14            right = mid - 1;
15        } else {
16            left = mid + 1;
17        }
18    }
19    return boundary_index;
20}
21
22template<typename T>
23std::vector<T> get_words() {
24    std::string line;
25    std::getline(std::cin, line);
26    std::istringstream ss{line};
27    ss >> std::boolalpha;
28    std::vector<T> v;
29    std::copy(std::istream_iterator<T>{ss}, std::istream_iterator<T>{}, std::back_inserter(v));
30    return v;
31}
32
33int main() {
34    std::vector<int> arr = get_words<int>();
35    int res = peak_of_mountain_array(arr);
36    std::cout << res << '\n';
37}
38

Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫