Skip to main content

376 - Wiggle Subsequence

Difficulty: Medium | Pattern: Greedy | Company tags: Google, Amazon

Problem Statement

A wiggle sequence is a sequence where the differences between successive numbers strictly alternate between positive and negative. The first difference (if one exists) may be either positive or negative. A sequence with fewer than two elements is trivially a wiggle sequence.

  • [1,7,4,9,2,5] is a wiggle sequence: differences are (6,-3,5,-7,3)
  • [1,4,7,2,5] is NOT: differences (3,3,-5,3) have consecutive positives

Given an integer array nums, return the length of the longest wiggle subsequence.

Example 1:

Input: nums = [1,7,4,9,2,5]
Output: 6

Example 2:

Input: nums = [1,17,5,10,13,15,10,5,16,8]
Output: 7
Explanation: e.g., [1,17,10,13,10,16,8]

Example 3:

Input: nums = [1,2,3,4,5,6,7,8,9]
Output: 2
Explanation: Only the first and last element.

Approach: Greedy — O(n), O(1)

Key insight: Greedily count direction changes. Track whether we need an "up" or "down" next. When consecutive elements in the same direction occur, ignore the interior ones — only the peak/valley matters.

Algorithm Flow

def wiggleMaxLength(nums: list[int]) -> int:
if len(nums) < 2:
return len(nums)

up = 1 # length of longest wiggle ending with an upward movement
down = 1 # length of longest wiggle ending with a downward movement

for i in range(1, len(nums)):
if nums[i] > nums[i-1]:
up = down + 1
elif nums[i] < nums[i-1]:
down = up + 1
# equal: no change

return max(up, down)

Dry Run

nums = [1,17,5,10,13,15,10,5,16,8]

inums[i-1]nums[i]Directionupdown
init11
1117up21
2175down23
3510up43
41013up43
51315up43
61510down45
7105down45
8516up65
9168down67

Result: max(6, 7) = 7

Why Equal Elements are Ignored

Equal adjacent elements don't contribute — they're not peaks or valleys and don't extend a wiggle sequence. The elif (not else) correctly skips them.

Edge Cases

  • Single element → 1
  • Two elements (equal) → 1
  • Two elements (different) → 2
  • All same → 1
  • Alternating → n (whole array is wiggle)

Complexity

  • Time: O(n)
  • Space: O(1)

Key Terms

TermDefinition
GreedyMaking the locally optimal choice at each step without backtracking, trusting it leads to a global optimum.
Wiggle sequenceA sequence whose consecutive differences strictly alternate in sign (positive, negative, positive, ...).
Peak/valleyA local maximum or minimum element — the only points that can extend a wiggle sequence.
Two-state trackingMaintaining separate running values (up, down) for "best sequence ending in an upward move" vs "ending in a downward move".

FAQ

Q: Why does the greedy approach work instead of needing full DP? A: Only direction changes (peaks and valleys) matter; monotonic runs collapse to their endpoints. Tracking up/down as the best wiggle length ending in each direction captures exactly this without needing to consider every subsequence.

Q: How does this relate to an O(n) DP formulation? A: The DP version defines up[i]/down[i] per index with the same transitions (up[i] = down[i-1]+1 if nums[i] > nums[i-1], similarly for down). The greedy code is just that DP compressed to O(1) space since only the previous state is needed.

Q: Can this be solved without extra space? A: Yes — the given solution already uses O(1) space with just two scalars, up and down.

Q: What if the array has all equal elements? A: Neither branch fires, so up and down stay at 1, correctly returning 1 (a single element is trivially the longest wiggle subsequence).

Q: How would you reconstruct the actual subsequence, not just its length? A: Track parent pointers or the actual indices chosen when updating up/down (or just record the sequence of direction changes as you scan), then backtrack from the larger of the two final states.

Quick Revision

  • Goal: length of the longest subsequence with alternating strictly increasing/decreasing differences.
  • Key insight: only peaks and valleys matter; flat/monotonic runs collapse.
  • Track two running values: up (best ending on an upward move) and down (best ending on a downward move).
  • On nums[i] > nums[i-1]: up = down + 1.
  • On nums[i] < nums[i-1]: down = up + 1.
  • On equal elements: no change (use elif, not else).
  • Initialize both to 1 (a single element is a trivial wiggle sequence).
  • Answer is max(up, down) at the end.
  • Time: O(n), Space: O(1) — a greedy compression of an O(n) DP.
  • 300 - Longest Increasing Subsequence — related sequence-DP problem also solvable greedily with optimization.
  • 135 - Candy — another greedy problem driven by comparing adjacent elements and local peaks/valleys.
  • Also related in pattern: "Best Time to Buy and Sell Stock II," which similarly exploits local peaks/valleys via a greedy scan.