A clear explanation of finding the longest subarray whose adjacent comparisons alternate between greater-than and less-than.
Problem Restatement
We are given an integer array arr.
We need to return the length of the longest contiguous subarray where the comparison signs between adjacent elements alternate.
That means the subarray must go up, down, up, down, or down, up, down, up.
For example:
[9, 4, 2, 10, 7]The comparisons are:
9 > 4
4 > 2
2 < 10
10 > 7This is not fully turbulent because the first two comparisons both go downward.
But this subarray is turbulent:
[4, 2, 10, 7]Its comparisons are:
4 > 2
2 < 10
10 > 7The signs alternate, so its length is 4.
The official constraints are 1 <= arr.length <= 4 * 10^4 and 0 <= arr[i] <= 10^9. The problem asks for the maximum length of a turbulent subarray.
Input and Output
| Item | Meaning |
|---|---|
| Input | An integer array arr |
| Output | Length of the longest turbulent subarray |
| Subarray | Must be contiguous |
| Key rule | Adjacent comparison signs must alternate |
Function shape:
def maxTurbulenceSize(arr: list[int]) -> int:
...Examples
Example 1:
arr = [9, 4, 2, 10, 7, 8, 8, 1, 9]One longest turbulent subarray is:
[4, 2, 10, 7, 8]The comparisons are:
4 > 2
2 < 10
10 > 7
7 < 8The signs alternate.
Answer:
5Example 2:
arr = [4, 8, 12, 16]Every comparison goes upward:
4 < 8
8 < 12
12 < 16No length 3 subarray is turbulent because the signs do not alternate.
Any two unequal adjacent numbers form a turbulent subarray of length 2.
Answer:
2Example 3:
arr = [100]A single element is a valid turbulent subarray of length 1.
Answer:
1First Thought: Check Every Subarray
The direct solution is to try every subarray and check whether its comparisons alternate.
For each starting index, extend the subarray to the right while the signs keep alternating.
class Solution:
def maxTurbulenceSize(self, arr: list[int]) -> int:
n = len(arr)
best = 1
for start in range(n):
for end in range(start + 1, n):
ok = True
for k in range(start + 1, end):
left = arr[k - 1]
mid = arr[k]
right = arr[k + 1]
if not ((left < mid > right) or (left > mid < right)):
ok = False
break
if ok:
best = max(best, end - start + 1)
return bestThis follows the definition directly.
Problem With Brute Force
The brute force solution checks many subarrays, and each subarray may require another scan.
That is too slow for an array length up to 4 * 10^4.
We need to process the array once and keep only the information needed for the current turbulent run.
Key Insight
For each adjacent pair, only three things can happen:
arr[i] > arr[i - 1]
arr[i] < arr[i - 1]
arr[i] == arr[i - 1]A turbulent subarray continues only when the current comparison has the opposite sign from the previous comparison.
So we can keep two lengths:
| Variable | Meaning |
|---|---|
up | Longest turbulent subarray ending at i where arr[i] > arr[i - 1] |
down | Longest turbulent subarray ending at i where arr[i] < arr[i - 1] |
If the current value is greater than the previous value, then the previous comparison must have been downward.
So:
up = down + 1
down = 1If the current value is smaller than the previous value, then the previous comparison must have been upward.
So:
down = up + 1
up = 1If the values are equal, turbulence breaks:
up = 1
down = 1Algorithm
Start with:
up = 1
down = 1
answer = 1Then scan from index 1 to the end.
For each index i:
- If
arr[i] > arr[i - 1], extend a previous downward run. - If
arr[i] < arr[i - 1], extend a previous upward run. - If
arr[i] == arr[i - 1], reset both lengths to1. - Update the answer with the larger of
upanddown.
Correctness
At every index i, up stores the length of the longest turbulent subarray ending at i whose last comparison is upward.
Similarly, down stores the length of the longest turbulent subarray ending at i whose last comparison is downward.
When arr[i] > arr[i - 1], the last comparison is upward. For the subarray to remain turbulent, the previous comparison must have been downward. Therefore, the best upward turbulent subarray ending at i has length down + 1 from the previous step.
When arr[i] < arr[i - 1], the same reasoning applies in the opposite direction. The best downward turbulent subarray ending at i has length up + 1 from the previous step.
When the two adjacent values are equal, there is no greater-than or less-than sign, so no turbulent subarray of length greater than 1 can continue through this pair. Both states reset to 1.
The algorithm updates the global answer after processing each index. Since every turbulent subarray has some ending index, and the algorithm considers the best valid turbulent subarray ending at each index, the maximum value recorded is the correct answer.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | We scan the array once |
| Space | O(1) | We store only a few variables |
Implementation
class Solution:
def maxTurbulenceSize(self, arr: list[int]) -> int:
up = 1
down = 1
answer = 1
for i in range(1, len(arr)):
if arr[i] > arr[i - 1]:
up = down + 1
down = 1
elif arr[i] < arr[i - 1]:
down = up + 1
up = 1
else:
up = 1
down = 1
answer = max(answer, up, down)
return answerCode Explanation
We initialize both states to 1:
up = 1
down = 1
answer = 1A single element is always a turbulent subarray of length 1.
Then we scan adjacent pairs:
for i in range(1, len(arr)):If the current value is larger:
if arr[i] > arr[i - 1]:then the last comparison is upward. It can only extend a previous downward state:
up = down + 1
down = 1If the current value is smaller:
elif arr[i] < arr[i - 1]:then the last comparison is downward. It can only extend a previous upward state:
down = up + 1
up = 1If the values are equal, the turbulent run breaks:
else:
up = 1
down = 1After each step, we update the best length:
answer = max(answer, up, down)Finally, return the longest length found:
return answerTesting
def run_tests():
s = Solution()
assert s.maxTurbulenceSize([9, 4, 2, 10, 7, 8, 8, 1, 9]) == 5
assert s.maxTurbulenceSize([4, 8, 12, 16]) == 2
assert s.maxTurbulenceSize([100]) == 1
assert s.maxTurbulenceSize([1, 1, 1]) == 1
assert s.maxTurbulenceSize([1, 2, 1, 2, 1]) == 5
assert s.maxTurbulenceSize([1, 2, 2, 1]) == 2
print("all tests passed")
run_tests()| Test | Expected | Why |
|---|---|---|
[9, 4, 2, 10, 7, 8, 8, 1, 9] | 5 | Standard mixed case |
[4, 8, 12, 16] | 2 | Monotonic increasing |
[100] | 1 | Single element |
[1, 1, 1] | 1 | Equal adjacent values break turbulence |
[1, 2, 1, 2, 1] | 5 | Entire array is turbulent |
[1, 2, 2, 1] | 2 | Equality splits the run |