Dynamic Programming · Medium
326. Longest Alternating Subsequence
Topics: Dynamic Programming
Solve it on DeVert Campus - free code runner
Problem
Given an array of n integers, find the length of its longest alternating (zig-zag) subsequence - one where the elements strictly alternate between rising and falling at every consecutive step (either up-down-up-... or down-up-down-...).
Input and output
Input: The first line contains an integer n. The second line contains n space-separated integers, the array.
Output: Print a single integer - the length of the longest alternating (zig-zag) subsequence.
Example
Input: 8 10 22 9 33 49 50 31 60 Output: 6
Constraints
1 <= n <= 10^4
Key observation
This problem has BOTH a full O(n²) DP solution (shown here, generalizable to related harder variants) AND a much simpler O(n) greedy solution (just counting alternating direction changes) - knowing both, and why the greedy one works here specifically, is a strong signal.
Intuition
Find the length of the longest subsequence that strictly alternates between rising and falling at every step.
Brute force: Try every possible subsequence directly and check if it alternates correctly, taking the longest - this is exponential (2^n subsequences), impractical for n up to 10^4.
Optimised: Tracking TWO separate arrays (one for 'ends in a rise', one for 'ends in a fall') captures both possible 'directions' a valid alternating subsequence could currently be in - cross-updating between them (a rise can only follow a fall, and vice versa) directly enforces the strict alternation requirement.
Optimal - Single Greedy Pass
Scan the array once while tracking a running count (starting at 1), and each time the direction between consecutive elements switches, relative to the previous comparison direction, whether the sequence is currently rising or falling, increment the count. Equal consecutive elements are simply skipped since they cannot contribute to either direction. This works because the longest alternating subsequence is exactly the sequence of local peaks and valleys in the array, and every direction change encountered while scanning left to right corresponds to exactly one extension of that subsequence, so no comparisons against earlier indices are ever needed.
Time: O(n)
Space: O(1)
Better - DP With Up/Down States
Maintain two DP arrays, up[i] and down[i], where up[i] is the length of the longest alternating subsequence ending at index i whose last move was an increase, and down[i] is the same but whose last move was a decrease. For each i, look back at every earlier index j: if arr[j] < arr[i], up[i] can be down[j] + 1, and if arr[j] > arr[i], down[i] can be up[j] + 1, taking the best over all such j. This is correct because any alternating subsequence ending at i must have been extended from a shorter alternating subsequence ending in the opposite direction at some earlier index, and checking all such j guarantees the true optimum is found, at the cost of a quadratic number of comparisons.
Time: O(n^2)
Space: O(n)
Brute Force - Enumerate All Subsequences
Generate every possible subsequence of the array (by choosing, for each element, whether to include it or not), check whether each generated subsequence strictly alternates between rising and falling at every step, and keep track of the longest one found that qualifies. This is correct because it exhaustively considers every candidate subsequence, but the number of subsequences grows exponentially with the array size, making it impractical beyond very small inputs.
Time: O(2^n)
Space: O(n)
Step by step
1. Track TWO arrays: inc[i] = longest alternating subsequence ending at i where the LAST STEP was a RISE, and dec[i] = same but where the last step was a FALL.
2. For j<i: if arr[j]<arr[i], inc[i] can be updated from dec[j]+1 (a fall INTO j, then a rise INTO i). If arr[j]>arr[i], dec[i] can be updated from inc[j]+1 (symmetric).
3. A single element alone has length 1 in both arrays; the final answer is the maximum anywhere across both.
Complexity
Time: O(n²) - for each index i, checking all earlier indices j.
Space: O(n), for the inc[] and dec[] arrays (though an O(n) GREEDY approach also exists, simply counting direction changes as you scan once, since maximizing alternation greedily by taking every local peak/valley is provably optimal here).
Edge cases
Test n = 1 (answer 1), an array with equal consecutive values (which can't extend either an up or down run), and n at the upper bound of 10^4.
Interview tip
Be ready to mention the simpler O(n) greedy alternative (count every local direction change: a strictly increasing-then-decreasing transition, or vice versa) as a more efficient solution than the O(n²) DP - a nice example of a problem that LOOKS like it needs full DP but actually has an elegant greedy shortcut.