The longest alternating subsequence
A sequence wiggles if the differences between successive elements are strictly positive and strictly negative
in turn (the first difference may be either). A single element wiggles trivially. Given nums, return the
length of its longest wiggling subsequence.
Examples
| Input | Output | Why |
|---|---|---|
nums = [1,7,4,9,2,5] | 6 | The whole list wiggles. |
nums = [1,17,5,10,13,15,10,5,16,8] | 7 | For example 1, 17, 10, 13, 10, 16, 8. |
nums = [1,2,3,4,5,6,7,8,9] | 2 | Any rise: one up-step. |
Constraints
- 1 ≤ nums.length ≤ 1000
- 0 ≤ nums[i] ≤ 1000
Hints
Hint 1
Two states per position: the longest wiggle ending here with an up-step, and with a down-step.
Hint 2
An up-step at i extends a down-ending wiggle; a down-step extends an up-ending one.
Hint 3
Only the best values so far matter, so two variables are enough.
Where people lose marks
- Counting zero differences as wiggles.
- Starting up and down at 0 instead of 1 (a single element is a wiggle of length 1).
Dry run on [1,17,5,10,13,15,10,5,16,8]
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 10nums10171521031341551065716889up1down1
A single element: up = down = 1.
-
Step 2 / 10nums10171521031341551065716889up2down1
1 → 17 rises: up = down + 1 = 2.
-
Step 3 / 10nums10171521031341551065716889up2down3
17 → 5 falls: down = up + 1 = 3.
-
Step 4 / 10nums10171521031341551065716889up4down3
5 → 10 rises: up = down + 1 = 4.
-
Step 5 / 10nums10171521031341551065716889up4down3
10 → 13 rises: up = down + 1 = 4.
-
Step 6 / 10nums10171521031341551065716889up4down3
13 → 15 rises: up = down + 1 = 4.
-
Step 7 / 10nums10171521031341551065716889up4down5
15 → 10 falls: down = up + 1 = 5.
-
Step 8 / 10nums10171521031341551065716889up4down5
10 → 5 falls: down = up + 1 = 5.
-
Step 9 / 10nums10171521031341551065716889up6down5
5 → 16 rises: up = down + 1 = 6.
-
Step 10 / 10nums10171521031341551065716889up6down7
16 → 8 falls: down = up + 1 = 7. The answer is max(up, down) = 7.
How to think about it
The subsequence pattern with a direction in the state. Let up be the longest wiggle seen so far whose last
step went up, and down the longest whose last step went down. When the next number is larger than the
previous one, any down-ending wiggle can be extended by an up-step: up = down + 1. When it is smaller,
down = up + 1. Equal neighbours change nothing. Because the best wiggle can always be shifted to end at the
latest extreme value, comparing with the immediately previous number suffices, which turns the O(n²) table into O(n).
Complexity. Time O(n) · Space O(1)
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
def wiggle_max_length(nums):
up = down = 1 # longest wiggle so far ending with a rise / with a fall
for prev, x in zip(nums, nums[1:]):
if x > prev:
up = down + 1 # a rise extends a wiggle that last fell
elif x < prev:
down = up + 1
return max(up, down)#include <algorithm>
#include <vector>
int solve(const std::vector<int>& nums) {
int up = 1, down = 1;
for (size_t i = 1; i < nums.size(); ++i) {
if (nums[i] > nums[i - 1]) up = down + 1; // a rise after a fall
else if (nums[i] < nums[i - 1]) down = up + 1; // a fall after a rise
}
return std::max(up, down);
}class Solution {
public int wiggleMaxLength(int[] nums) {
int up = 1, down = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] > nums[i - 1]) up = down + 1; // a rise after a fall
else if (nums[i] < nums[i - 1]) down = up + 1; // a fall after a rise
}
return Math.max(up, down);
}
}function wiggleMaxLength(nums) {
let up = 1, down = 1;
for (let i = 1; i < nums.length; i++) {
if (nums[i] > nums[i - 1]) up = down + 1; // a rise after a fall
else if (nums[i] < nums[i - 1]) down = up + 1; // a fall after a rise
}
return Math.max(up, down);
}fn solve(nums: &[i64]) -> i64 {
let (mut up, mut down) = (1i64, 1i64);
for w in nums.windows(2) {
if w[1] > w[0] {
up = down + 1; // a rise after a fall
} else if w[1] < w[0] {
down = up + 1; // a fall after a rise
}
}
up.max(down)
}solve :: [Int] -> Int
solve nums = uncurry max (foldl step (1, 1) (zip nums (drop 1 nums)))
where
-- (longest ending with a rise, longest ending with a fall)
step (up, down) (prev, x)
| x > prev = (down + 1, down)
| x < prev = (up, up + 1)
| otherwise = (up, down)
Variants to try
- The O(n²) version with explicit end[i] tables, as a stepping stone.
- Return the subsequence.
- Require differences of at least k in absolute value.
Next problems
The largest chain of divisors
Find the size of the largest subset of distinct positive integers in which every pair divides one way or the other. Sort first, and divisibility becomes a chain.
Open the problem → Medium25 minHow many longest increasing subsequences
Count the strictly increasing subsequences of maximum length. Keep, for each position, the best length ending there and how many subsequences achieve it.
Open the problem → Medium20 minThe longest rise-then-fall subsequence
Return the length of the longest subsequence that strictly increases and then strictly decreases. Run the increasing table from both ends and join them at the peak.
Open the problem →Write a solution and run it against the real test table.
Keep going
The largest chain of divisors
Find the size of the largest subset of distinct positive integers in which every pair divides one way or the other. Sort first, and divisibility becomes a chain.
Open the problem → Medium25 minHow many longest increasing subsequences
Count the strictly increasing subsequences of maximum length. Keep, for each position, the best length ending there and how many subsequences achieve it.
Open the problem → Medium20 minThe longest rise-then-fall subsequence
Return the length of the longest subsequence that strictly increases and then strictly decreases. Run the increasing table from both ends and join them at the peak.
Open the problem → Dynamic programmingThe pattern behind it
Write the recursion, notice the repeats, fill a table in an order that makes each entry final.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.