Monotonic Deque - Intuition Guide¶
The Mental Model: The VIP Queue¶
Imagine a VIP queue where only the "best" candidates stay: - New candidates join from the back - When someone better arrives, weaker candidates leave - The front always shows the current best - Old candidates eventually time out and leave
This is exactly how a monotonic deque works for sliding window problems.
Why Monotonic Deque?¶
A heap gives O(log k) per operation. But with a deque, we achieve O(1) amortized: - Each element enters once, exits once - The front is always the answer - No need to search through the structure
Core Insight¶
When looking for the maximum in a window: - If element B comes after A and B >= A, then A will never be the maximum for any future window - A is "dominated" by B and can be removed
Pattern 1: Fixed Window Maximum (LC 239)¶
The insight: Maintain a decreasing deque. Front = current window max.
nums = [1, 3, -1, -3, 5, 3, 6, 7], k = 3
Step 1: [1] dq=[0] window incomplete
Step 2: [1,3] dq=[1] 3 dominates 1, pop it
Step 3: [1,3,-1] dq=[1,2] window complete, max=3
Step 4: [3,-1,-3] dq=[1,2,3] max=3 (index 1 still valid)
Step 5: [-1,-3,5] dq=[4] 5 dominates all, max=5
...
The code:
Pattern 2: Two Deques (LC 1438)¶
The insight: For max-min constraint, maintain both max and min deques.
nums = [8, 2, 4, 7], limit = 4
max_dq min_dq max-min valid?
i=0: [8] [0] [0] 0 β
i=1: [8,2] [0,1] [1,0] 6 β shrink!
[2] [1] [1] 0 β
i=2: [2,4] [1,2] [1,2] 2 β
i=3: [2,4,7] [3,2] [1,2,3] 5 β shrink!
[4,7] [3,2] [2,3] 3 β
Longest: 2
Pattern 3: Prefix Sum + Deque (LC 862)¶
The insight: With negative numbers, use prefix sums and find minimum prefix.
nums = [2, -1, 2], k = 3
prefix = [0, 2, 1, 3]
For j=3 (prefix=3):
Find smallest i where prefix[3] - prefix[i] >= 3
prefix[3] - prefix[2] = 3 - 1 = 2 < 3
prefix[3] - prefix[0] = 3 - 0 = 3 >= 3 β
Length = 3 - 0 = 3
Why increasing deque?
If prefix[i1] >= prefix[i2] where i1 < i2:
- i2 gives larger difference (prefix[j] - prefix[i2] >= prefix[j] - prefix[i1])
- i2 gives shorter length (j - i2 < j - i1)
- i1 is dominated, remove it
Pattern 4: Algebraic Transformation (LC 1499)¶
The insight: Rewrite the equation to separate the indices.
Maximize: yi + yj + |xi - xj|
Since sorted and i < j:
= yi + yj + (xj - xi)
= (yj + xj) + (yi - xi)
^^^^^^^^ ^^^^^^^^^
current j maximize for i in window
Now it's a standard sliding window maximum on yi - xi!
Common Mistakes¶
Mistake 1: Wrong comparison operator¶
# β Wrong for max deque: using <=
while dq and nums[dq[-1]] <= num: # Removes equal elements
# β
Right: use < for max (keep equal elements)
while dq and nums[dq[-1]] < num:
Mistake 2: Not removing stale elements¶
# β Wrong: forgetting to remove out-of-window elements
dq.append(i)
result.append(nums[dq[0]])
# β
Right: remove stale elements first
while dq and dq[0] < i - k + 1:
dq.popleft()
dq.append(i)
Mistake 3: Wrong window condition¶
# β Wrong: off-by-one in window check
if i >= k:
# β
Right: window complete when i >= k-1
if i >= k - 1:
Quick Pattern Recognition¶
| Clue | Pattern |
|---|---|
| "maximum/minimum in sliding window" | Single deque |
| "longest subarray with max-min <= limit" | Two deques |
| "shortest subarray with sum >= k" (negatives) | Prefix + deque |
| "maximize yi + yj + distance" | Transform + deque |
Monotonic Deque vs Monotonic Stack¶
| Structure | Use Case | Direction |
|---|---|---|
| Stack | Next greater/smaller element | Push/pop from one end |
| Deque | Window maximum/minimum | Push one end, pop both ends |
Visual Summary¶
Monotonic Deque (Decreasing for Max):
New element arrives:
βββββββββββββββββββββββββββββββ
Remove stale β Remove dominated elements β Add new
from front β from back β to back
β β β β β
βββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββ
β MAX β ... β ... β dominated β NEW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
Answer is These get
always here removed