Valid Mountain Array: Linear Scan
Given an array of integers arr, return true if and only if it is a valid mountain array. An array is a mountain if it has at least three elements, there exists some index i with 0 < i < arr.length - 1 such that the values strictly increase from arr[0] to arr[i], and then strictly decrease from arr[i] to arr[arr.length - 1].
This is LeetCode 941: Valid Mountain Array. The problem is asking you to verify a specific shape: values go up, hit a peak, then come back down. No plateaus, no flat spots, and the peak cannot be at the very start or end.
Why brute force is overkill
You could try checking every possible peak index and verifying that the left side is strictly increasing and the right side is strictly decreasing. That works, but it is more complex than necessary. A simpler mental model is to walk through the array from left to right in two phases: first climbing up, then climbing down.
The approach: climb up, then climb down
Think of yourself walking along the array from the left:
- Climb up while consecutive values are strictly increasing. Keep moving the pointer
iforward as long asarr[i] < arr[i + 1]. - Check the peak. If
iis still at 0, you never climbed up at all, so the array has no ascending portion. Ifiis at the last index, the array only goes up and never comes back down. Either case means it is not a mountain. - Climb down while consecutive values are strictly decreasing. Keep moving
iforward as long asarr[i] > arr[i + 1]. - Check the end. If
ireached the last index, you successfully climbed all the way down. The array is a valid mountain.
That is the entire algorithm. One pointer, one pass, no extra data structures.
Python solution
def validMountainArray(arr):
n = len(arr)
if n < 3:
return False
i = 0
while i + 1 < n and arr[i] < arr[i + 1]:
i += 1
if i == 0 or i == n - 1:
return False
while i + 1 < n and arr[i] > arr[i + 1]:
i += 1
return i == n - 1
The variable i walks forward through the array twice. The first while loop handles the ascending phase, and the second handles the descending phase. If i reaches the end after both phases, the array is a valid mountain.
Let's trace through the example step by step.
Step 1: Check length. Array must have at least 3 elements.
Length is 5, which is at least 3. We can proceed.
Step 2: Walk up from the left while strictly increasing.
i starts at 0. arr[0]=0 < arr[1]=3, so i becomes 1. arr[1]=3 < arr[2]=5, so i becomes 2. arr[2]=5 is not less than arr[3]=4, so we stop. i = 2.
Step 3: Verify the peak is not at the start or end.
i = 2, which is not 0 and not 4 (last index). The peak is in the middle. Good.
Step 4: Walk down from the peak while strictly decreasing.
From i=2: arr[2]=5 > arr[3]=4, so i becomes 3. arr[3]=4 > arr[4]=2, so i becomes 4. We reached the last index.
Step 5: Check if we reached the end.
i = 4, which equals len(arr) - 1. We climbed all the way down to the end. This is a valid mountain array.
Why the boundary checks matter
The two checks i == 0 and i == n - 1 after the ascending phase are critical:
- If
i == 0, the first element is not less than the second. That means there is no ascending portion at all. An array like[5, 4, 3, 2, 1]would fail here. - If
i == n - 1, every consecutive pair was strictly increasing, so the pointer walked all the way to the end. An array like[1, 2, 3, 4, 5]would fail here.
Both cases disqualify the array because a mountain needs both a proper uphill and a proper downhill.
The same pointer i is used for both phases. After climbing up, i is sitting at the peak. Then climbing down starts from that same position. This avoids the need to find the peak separately.
Complexity analysis
| Approach | Time | Space |
|---|---|---|
| Linear scan (climb up, climb down) | O(n) | O(1) |
Each element is visited at most once across the two while loops. The pointer only moves forward, so the total work is linear. No extra space is needed beyond the index variable.
The building blocks
Linear scan with phases
This problem demonstrates a clean pattern: scanning an array in distinct phases with a single pointer. Instead of maintaining complex state or checking conditions globally, you break the traversal into stages. Each stage has a simple loop condition, and the transitions between stages handle the validation. This same idea appears in problems where you need to verify that an array follows some structural rule (sorted, bitonic, alternating).
Boundary validation
Checking that the peak is not at the first or last index is a common boundary check. Many array problems require you to verify that some feature does not occur at the edges. Getting these checks right often makes the difference between a correct and an almost-correct solution.
Edge cases
- Array length less than 3. Return
falseimmediately. A mountain needs at least three elements: one going up, a peak, and one going down. - Strictly increasing only. For example,
[1, 2, 3, 4]. The ascending phase walksito the last index, and the checki == n - 1catches this. - Strictly decreasing only. For example,
[4, 3, 2, 1]. The ascending phase does not moveiat all, and the checki == 0catches this. - Plateau at the peak. For example,
[1, 3, 3, 2]. The ascending phase stops whenarr[i]is not strictly less thanarr[i + 1](at the first 3). Theniis at index 1. The descending phase checks for strict decrease:arr[1] = 3is not strictly greater thanarr[2] = 3, so the descending loop does not start.istays at 1, which is notn - 1, so we returnfalse. - All equal elements. For example,
[3, 3, 3]. The ascending phase does not advanceiat all, andi == 0returnsfalse.
From understanding to recall
The valid mountain array problem has a deceptively simple solution. You might remember "walk up then walk down" but forget the boundary checks that catch edge cases. Or you might second-guess whether the comparisons should be strict or non-strict (they must be strict).
Spaced repetition helps you internalize these details. You write the solution, test it against the edge cases, and then revisit it days later. After a few reps, the two-phase scan with boundary checks becomes second nature. The anchor to hold onto: one pointer, two loops, two boundary checks.
CodeBricks builds review schedules around problems like this so you practice the exact details that tend to fade. Each drill reinforces the pattern until writing it from memory feels effortless.
Related posts
- Peak Index in a Mountain Array - Finding the peak of a mountain array using binary search
- Longest Mountain in Array - Finding the longest mountain subsequence within an array
- Monotonic Array - Checking if an array is entirely non-decreasing or non-increasing