A clear explanation of the Longest Mountain in Array problem using peak detection and two-pointer expansion.
Problem Restatement
We are given an integer array arr.
A mountain is a contiguous subarray that:
| Rule | Meaning |
|---|---|
| Length | Has at least 3 elements |
| Up slope | Strictly increases before the peak |
| Peak | Has one element greater than both neighbors |
| Down slope | Strictly decreases after the peak |
Return the length of the longest mountain.
If there is no mountain, return 0.
Input and Output
| Item | Meaning |
|---|---|
| Input | Integer array arr |
| Output | Length of the longest mountain subarray |
| Subarray | Must be contiguous |
| Strictness | Equal adjacent values break a mountain |
| Minimum length | 3 |
Function shape:
class Solution:
def longestMountain(self, arr: list[int]) -> int:
...Examples
Example 1:
arr = [2, 1, 4, 7, 3, 2, 5]The longest mountain is:
[1, 4, 7, 3, 2]It strictly increases:
1 < 4 < 7Then strictly decreases:
7 > 3 > 2Its length is:
5So the answer is:
5Example 2:
arr = [2, 2, 2]There is no strict increase or strict decrease.
So the answer is:
0First Thought: Check Every Subarray
A brute force solution is to try every subarray of length at least 3.
For each subarray:
- Find whether there is a valid peak.
- Check that values strictly increase before the peak.
- Check that values strictly decrease after the peak.
- Update the best length.
This works, but it is too slow because there are many subarrays.
Problem With Brute Force
There are O(n^2) subarrays.
Checking whether one subarray is a mountain can take O(n) time.
So the total time can become:
O(n^3)We need to use the structure of a mountain directly.
Key Insight
Every mountain has a peak.
A peak is an index i such that:
arr[i - 1] < arr[i] > arr[i + 1]Once we find a peak, we can expand left while the values are strictly increasing toward the peak, and expand right while the values are strictly decreasing away from the peak.
That gives the full mountain centered at this peak.
Algorithm
Scan the array from left to right.
For each index i from 1 to n - 2:
- Check whether
iis a peak. - If not, continue.
- If it is a peak:
- Move
leftleft whilearr[left - 1] < arr[left]. - Move
rightright whilearr[right] > arr[right + 1]. - Compute the mountain length:
right - left + 1 - Update the answer.
- Move
itorightbecause all indices inside this mountain have already been handled.
- Move
Walkthrough
Use:
arr = [2, 1, 4, 7, 3, 2, 5]Check index 1:
2, 1, 41 is not a peak.
Check index 2:
1, 4, 74 is not a peak.
Check index 3:
4, 7, 37 is a peak.
Expand left:
1 < 4 < 7Expand right:
7 > 3 > 2The mountain is:
[1, 4, 7, 3, 2]Its length is:
5The answer becomes 5.
No later longer mountain exists, so return:
5Correctness
A valid mountain must have a peak, because it must strictly increase first and strictly decrease afterward. Therefore, the maximum mountain can be found by checking possible peak positions.
When the algorithm finds a peak i, it expands left exactly while the slope remains strictly increasing toward i. It stops at the first position where extending farther would break strict increase or leave the array.
It expands right exactly while the slope remains strictly decreasing away from i. It stops at the first position where extending farther would break strict decrease or leave the array.
So the interval [left, right] is exactly the longest mountain with peak i.
The algorithm checks every possible peak or skips only indices already inside a mountain whose full right boundary has been processed. Such skipped indices cannot start a longer mountain with a different peak inside the same strictly decreasing side.
Thus every valid mountain’s peak is considered, and the maximum length recorded is the length of the longest mountain.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | Each index is scanned a constant number of times |
| Space | O(1) | Only pointers and counters are used |
Implementation
class Solution:
def longestMountain(self, arr: list[int]) -> int:
n = len(arr)
answer = 0
i = 1
while i < n - 1:
is_peak = arr[i - 1] < arr[i] > arr[i + 1]
if not is_peak:
i += 1
continue
left = i
right = i
while left > 0 and arr[left - 1] < arr[left]:
left -= 1
while right + 1 < n and arr[right] > arr[right + 1]:
right += 1
answer = max(answer, right - left + 1)
i = right
return answerCode Explanation
We start checking from index 1 because a peak cannot be the first element:
i = 1A valid peak must have a smaller neighbor on both sides:
is_peak = arr[i - 1] < arr[i] > arr[i + 1]If i is not a peak, move forward:
i += 1If i is a peak, expand left:
while left > 0 and arr[left - 1] < arr[left]:
left -= 1Then expand right:
while right + 1 < n and arr[right] > arr[right + 1]:
right += 1The mountain length is:
right - left + 1After processing this mountain, we move i to right:
i = rightThe next loop iteration will advance from there if it is not a peak.
Testing
def run_tests():
s = Solution()
assert s.longestMountain([2, 1, 4, 7, 3, 2, 5]) == 5
assert s.longestMountain([2, 2, 2]) == 0
assert s.longestMountain([1, 2, 3]) == 0
assert s.longestMountain([3, 2, 1]) == 0
assert s.longestMountain([1, 2, 1]) == 3
assert s.longestMountain([1, 2, 3, 2, 1, 2, 3, 4, 1]) == 5
assert s.longestMountain([1, 2, 2, 1]) == 0
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
| Standard example | Confirms normal mountain detection |
| All equal | Confirms strictness |
| Only increasing | Confirms no down slope |
| Only decreasing | Confirms no up slope |
| Minimum mountain | Confirms length 3 is valid |
| Multiple mountains | Confirms longest one is returned |
| Plateau near peak | Confirms equal adjacent values break a mountain |