- API Kernel:
MonotonicDeque - Why Monotonic Deque?
- Core Insight
- Universal Template Structure
- Pattern Variants
- Problem Link
- Difficulty
- Tags
- Pattern
- API Kernel
- Problem Summary
- Key Insight
- Template Mapping
- Complexity
- Why This Problem First?
- Common Mistakes
- Related Problems
- Problem Link
- Difficulty
- Tags
- Pattern
- API Kernel
- Problem Summary
- Key Insight
- Template Mapping
- Complexity
- Why This Problem Second?
- Common Mistakes
- Related Problems
- Problem Link
- Difficulty
- Tags
- Pattern
- API Kernel
- Problem Summary
- Key Insight
- Template Mapping
- Complexity
- Why This Problem Third?
- Common Mistakes
- Related Problems
- Problem Link
- Difficulty
- Tags
- Pattern
- API Kernel
- Problem Summary
- Key Insight
- Template Mapping
- Complexity
- Why This Problem Fourth?
- Common Mistakes
- Related Problems
- Problem Comparison
- Pattern Evolution
- Key Differences
- Decision Tree
- Pattern Selection Guide
- Monotonic Deque vs Other Approaches
- Key Indicators for Monotonic Deque
- Universal Templates
- Quick Reference
Core Mechanism: Maintain a deque of indices where values are monotonic (increasing or decreasing), enabling O(1) access to window extrema (max/min) while supporting efficient front/back operations.
Monotonic Deque solves problems where:
- You need the maximum or minimum within a sliding window
- Window size can be fixed or variable
- You need to query extrema as the window moves
The key insight is that when a new element enters the window:
- Remove stale elements from the front (out of window range)
- Remove dominated elements from the back (will never be the answer)
- The front always contains the answer for the current window
For a max deque: if nums[i] >= nums[j] where i > j, then j will never be the maximum for any window containing i. So we can safely remove j from the deque.
from collections import deque
def monotonic_deque_max(nums: list, k: int) -> list:
"""Sliding window maximum with window size k."""
dq = deque() # Store indices
result = []
for i, num in enumerate(nums):
# Remove indices out of window
while dq and dq[0] < i - k + 1:
dq.popleft()
# Remove dominated elements (for max deque)
while dq and nums[dq[-1]] <= num:
dq.pop()
dq.append(i)
# Window is fully formed
if i >= k - 1:
result.append(nums[dq[0]])
return result| Pattern | Deque Order | Use Case | Example |
|---|---|---|---|
| Sliding Max | Decreasing | Maximum in window | LC 239 |
| Sliding Min | Increasing | Minimum in window | LC 1438 |
| Prefix Sum + Deque | Increasing | Min/max prefix for subarray | LC 862 |
| Pair Optimization | Custom | Optimize pair selection | LC 1499 |
https://leetcode.com/problems/sliding-window-maximum/
Hard
- Array
- Queue
- Sliding Window
- Heap (Priority Queue)
- Monotonic Queue
Monotonic Deque - Sliding Maximum
MonotonicDeque
Given an integer array nums and a sliding window of size k moving from left to right, return an array of the maximum value in each window position.
A max-heap would give O(log k) per element. But with a monotonic deque, we achieve O(1) amortized:
- Elements enter the deque once and exit at most once
- The front always contains the current window's maximum
- Dominated elements are removed immediately
The deque maintains decreasing order: if we're looking for max, any smaller elements that came before the current element are useless.
from collections import deque
def maxSlidingWindow(nums: list, k: int) -> list:
dq = deque() # Store indices, values are decreasing
result = []
for i, num in enumerate(nums):
# Remove out-of-window elements from front
while dq and dq[0] < i - k + 1:
dq.popleft()
# Remove smaller elements from back (they'll never be max)
while dq and nums[dq[-1]] < num:
dq.pop()
dq.append(i)
# Window is complete (has k elements)
if i >= k - 1:
result.append(nums[dq[0]])
return result- Time: O(n) - each element enters and exits deque at most once
- Space: O(k) - deque stores at most k elements
This is the canonical monotonic deque problem:
- Fixed window size - simplest case
- Pure max query - no additional computation
- Clear demonstration of the "remove dominated" principle
- Using
<=vs<- For max, use<to remove strictly smaller; for min, use> - Forgetting to remove stale elements - Must check
dq[0] < i - k + 1 - Not waiting for full window - Only add to result when
i >= k - 1
- LC 1438: Longest Continuous Subarray (Min-max deque)
- LC 862: Shortest Subarray with Sum at Least K (Prefix sum + deque)
- LC 1696: Jump Game VI (DP with monotonic deque)
https://leetcode.com/problems/longest-continuous-subarray-with-absolute-diff-limit/
Medium
- Array
- Queue
- Sliding Window
- Heap (Priority Queue)
- Monotonic Queue
Monotonic Deque - Two Deques (Max + Min)
MonotonicDeque
Given an array of integers nums and an integer limit, return the size of the longest non-empty subarray such that the absolute difference between any two elements is less than or equal to limit.
The absolute difference between any two elements in a subarray equals max - min of that subarray. So we need to find the longest window where max - min <= limit.
We maintain two deques:
- One for maximum (decreasing order)
- One for minimum (increasing order)
When max - min > limit, shrink window from the left.
from collections import deque
def longestSubarray(nums: list, limit: int) -> int:
max_dq = deque() # Decreasing: front is max
min_dq = deque() # Increasing: front is min
left = 0
result = 0
for right, num in enumerate(nums):
# Maintain max deque
while max_dq and nums[max_dq[-1]] < num:
max_dq.pop()
max_dq.append(right)
# Maintain min deque
while min_dq and nums[min_dq[-1]] > num:
min_dq.pop()
min_dq.append(right)
# Shrink window if constraint violated
while nums[max_dq[0]] - nums[min_dq[0]] > limit:
left += 1
if max_dq[0] < left:
max_dq.popleft()
if min_dq[0] < left:
min_dq.popleft()
result = max(result, right - left + 1)
return result- Time: O(n) - each element enters/exits each deque at most once
- Space: O(n) - deques can grow to size n
This problem extends the base pattern:
- Two deques instead of one
- Variable window size instead of fixed
- Constraint-based shrinking instead of fixed-size sliding
- Only checking one deque - Must remove from both when shrinking
- Wrong comparison - Max deque removes smaller, min deque removes larger
- Off-by-one in left pointer - Must check
dq[0] < left, not<= left
- LC 239: Sliding Window Maximum (Single deque, fixed window)
- LC 1425: Constrained Subsequence Sum (DP + deque)
- LC 1696: Jump Game VI (DP + deque)
https://leetcode.com/problems/shortest-subarray-with-sum-at-least-k/
Hard
- Array
- Binary Search
- Queue
- Sliding Window
- Heap (Priority Queue)
- Prefix Sum
- Monotonic Queue
Monotonic Deque - Prefix Sum Optimization
MonotonicDeque
Given an integer array nums and an integer k, return the length of the shortest non-empty subarray with sum at least k. Return -1 if no such subarray exists.
With negative numbers, we can't use simple sliding window. Instead:
- Use prefix sums:
sum(nums[i:j]) = prefix[j] - prefix[i] - For each
j, find smallesti < jwhereprefix[j] - prefix[i] >= k - Maintain increasing monotonic deque of prefix sums
Why increasing? If prefix[i1] >= prefix[i2] where i1 < i2, then i1 is dominated: using i2 gives both a larger sum difference and a shorter subarray.
from collections import deque
def shortestSubarray(nums: list, k: int) -> int:
n = len(nums)
prefix = [0] * (n + 1)
for i in range(n):
prefix[i + 1] = prefix[i] + nums[i]
dq = deque() # Indices with increasing prefix values
result = float('inf')
for j in range(n + 1):
# Try to find valid subarray ending at j
while dq and prefix[j] - prefix[dq[0]] >= k:
result = min(result, j - dq.popleft())
# Maintain increasing order
while dq and prefix[dq[-1]] >= prefix[j]:
dq.pop()
dq.append(j)
return result if result != float('inf') else -1- Time: O(n) - each index enters and exits deque at most once
- Space: O(n) - prefix array and deque
This problem combines multiple concepts:
- Prefix sum transformation - convert to prefix differences
- Monotonic deque for optimization - find best starting index
- Handles negative numbers - unlike simple sliding window
- Forgetting prefix[0] = 0 - Need index 0 for subarrays starting from beginning
- Wrong deque order - Must be increasing for this problem
- Not popping found elements - Once we find a valid
i, we pop it (won't give shorter answer for laterj)
- LC 209: Minimum Size Subarray Sum (Positive only, simpler)
- LC 560: Subarray Sum Equals K (Count, not length)
- LC 1074: Number of Submatrices That Sum to Target (2D extension)
https://leetcode.com/problems/max-value-of-equation/
Hard
- Array
- Queue
- Sliding Window
- Heap (Priority Queue)
- Monotonic Queue
Monotonic Deque - Pair Optimization
MonotonicDeque
Given points (xi, yi) sorted by x-coordinate and an integer k, find the maximum value of yi + yj + |xi - xj| where |xi - xj| <= k and i < j.
Since points are sorted by x and i < j, we have xj >= xi, so |xi - xj| = xj - xi.
The equation becomes: yi + yj + xj - xi = (yj + xj) + (yi - xi)
For each point j, we want to maximize yi - xi among all valid i (where xj - xi <= k).
This is exactly a sliding window maximum problem:
- Window constraint:
xj - xi <= k - Maximize:
yi - xi
from collections import deque
def findMaxValueOfEquation(points: list, k: int) -> int:
dq = deque() # Store (x, y-x) with decreasing y-x
result = float('-inf')
for x, y in points:
# Remove points outside window (xj - xi > k)
while dq and x - dq[0][0] > k:
dq.popleft()
# Calculate answer if we have valid candidates
if dq:
result = max(result, y + x + dq[0][1]) # yj + xj + (yi - xi)
# Maintain decreasing order of y-x
while dq and dq[-1][1] <= y - x:
dq.pop()
dq.append((x, y - x))
return result- Time: O(n) - each point enters and exits deque at most once
- Space: O(n) - deque can grow to size n
This problem shows the pattern's versatility:
- Algebraic transformation - restructure equation to fit pattern
- Non-obvious window - constraint is on x-difference, not index
- Store derived values - deque stores
y-x, not original values
- Wrong window condition - Check
x - dq[0][0] > k, not>= k - Order of operations - Update answer BEFORE adding current point
- Missing edge case - Need at least one valid point in deque before computing answer
- LC 239: Sliding Window Maximum (Basic deque)
- LC 1696: Jump Game VI (DP + deque)
- LC 1425: Constrained Subsequence Sum (DP + deque)
| Problem | Core Pattern | Deque Order | Window Type | Key Insight |
|---|---|---|---|---|
| LC 239 Sliding Max | Single max deque | Decreasing | Fixed size | Front = current max |
| LC 1438 Max-Min Limit | Two deques (max+min) | Decreasing + Increasing | Variable | Shrink when max-min > limit |
| LC 862 Shortest Sum >= K | Prefix sum + deque | Increasing | Variable | Pop when valid, handles negatives |
| LC 1499 Max Equation | Transform + deque | Decreasing | x-distance | Rewrite equation to fit pattern |
LC 239 Sliding Window Maximum
│
│ Add second deque for min
│ Variable window size
↓
LC 1438 Longest Subarray (Max-Min <= Limit)
│
│ Add prefix sum transformation
│ Handle negative numbers
↓
LC 862 Shortest Subarray with Sum >= K
│
│ Algebraic transformation
│ Non-index-based window
↓
LC 1499 Max Value of Equation
| Problem | Order | Why |
|---|---|---|
| LC 239, 1499 | Decreasing | Need maximum value |
| LC 1438 | Both | Need both max and min |
| LC 862 | Increasing | Minimize prefix for max difference |
| Problem | When to Remove from Front |
|---|---|
| LC 239 | Index out of fixed window |
| LC 1438 | Index before left pointer |
| LC 862 | After using for valid answer |
| LC 1499 | x-distance exceeds k |
| Problem | Stores |
|---|---|
| LC 239 | Indices |
| LC 1438 | Indices |
| LC 862 | Indices (into prefix array) |
| LC 1499 | (x, y-x) tuples |
Start: Need window extrema (max/min)?
│
▼
┌───────────────────┐
│ Fixed or variable │
│ window size? │
└───────────────────┘
│
┌───────┴───────┐
▼ ▼
Fixed Variable
│ │
▼ ▼
LC 239 Need both
Single max AND min?
deque │
┌───┴───┐
▼ ▼
Yes No
│ │
▼ ▼
LC 1438 Negative
Two numbers?
deques │
┌───┴───┐
▼ ▼
Yes No
│ │
▼ ▼
LC 862 Simple
Prefix sliding
sum window
- Fixed window size
- Need maximum in each window
- Standard sliding window
- Need both max and min simultaneously
- Constraint involves max-min difference
- Variable window based on constraint
- Subarray sum problems
- Array has negative numbers
- Need shortest subarray with sum >= k
- Equation can be rewritten to separate indices
- Window based on non-index metric (distance, time)
- Pair optimization problems
| Approach | Use When | Complexity |
|---|---|---|
| Monotonic Deque | Sliding window max/min | O(n) |
| Heap | Dynamic max/min, no window constraint | O(n log n) |
| Segment Tree | Random access range queries | O(n log n) |
| Monotonic Stack | Next greater/smaller element | O(n) |
| Clue | Pattern |
|---|---|
| "sliding window maximum/minimum" | LC 239 |
| "longest subarray with max-min <= limit" | LC 1438 |
| "shortest subarray with sum >= k" (with negatives) | LC 862 |
| "maximize expression with distance constraint" | LC 1499 |
from collections import deque
def sliding_window_max(nums: list, k: int) -> list:
"""
Return maximum in each window of size k.
Time: O(n), Space: O(k)
"""
dq = deque() # Store indices, values decreasing
result = []
for i, num in enumerate(nums):
# Remove out-of-window indices
while dq and dq[0] < i - k + 1:
dq.popleft()
# Remove smaller elements (they're dominated)
while dq and nums[dq[-1]] < num:
dq.pop()
dq.append(i)
if i >= k - 1:
result.append(nums[dq[0]])
return resultUse for: LC 239, fixed-size window extrema
from collections import deque
def longest_subarray_max_min(nums: list, limit: int) -> int:
"""
Longest subarray where max - min <= limit.
Time: O(n), Space: O(n)
"""
max_dq = deque() # Decreasing
min_dq = deque() # Increasing
left = 0
result = 0
for right, num in enumerate(nums):
# Maintain max deque
while max_dq and nums[max_dq[-1]] < num:
max_dq.pop()
max_dq.append(right)
# Maintain min deque
while min_dq and nums[min_dq[-1]] > num:
min_dq.pop()
min_dq.append(right)
# Shrink if constraint violated
while nums[max_dq[0]] - nums[min_dq[0]] > limit:
left += 1
if max_dq[0] < left:
max_dq.popleft()
if min_dq[0] < left:
min_dq.popleft()
result = max(result, right - left + 1)
return resultUse for: LC 1438, problems needing both max and min
from collections import deque
def shortest_subarray_sum_k(nums: list, k: int) -> int:
"""
Shortest subarray with sum >= k (handles negatives).
Time: O(n), Space: O(n)
"""
n = len(nums)
prefix = [0] * (n + 1)
for i in range(n):
prefix[i + 1] = prefix[i] + nums[i]
dq = deque() # Increasing prefix values
result = float('inf')
for j in range(n + 1):
# Find valid subarray
while dq and prefix[j] - prefix[dq[0]] >= k:
result = min(result, j - dq.popleft())
# Maintain increasing order
while dq and prefix[dq[-1]] >= prefix[j]:
dq.pop()
dq.append(j)
return result if result != float('inf') else -1Use for: LC 862, subarray sum with negative numbers
from collections import deque
def max_value_equation(points: list, k: int) -> int:
"""
Maximize yi + yj + |xi - xj| where |xi - xj| <= k.
Points sorted by x. Rewrite as (yj + xj) + (yi - xi).
Time: O(n), Space: O(n)
"""
dq = deque() # (x, y-x) with decreasing y-x
result = float('-inf')
for x, y in points:
# Remove points outside window
while dq and x - dq[0][0] > k:
dq.popleft()
# Calculate answer with best candidate
if dq:
result = max(result, y + x + dq[0][1])
# Maintain decreasing y-x
while dq and dq[-1][1] <= y - x:
dq.pop()
dq.append((x, y - x))
return resultUse for: LC 1499, pair optimization with distance constraint
| Problem Type | Template | Deque Order | Key Step |
|---|---|---|---|
| Fixed window max/min | Template 1 | Decreasing/Increasing | Remove out-of-window |
| Variable window constraint | Template 2 | Both | Shrink when violated |
| Subarray sum (negatives) | Template 3 | Increasing | Pop when valid |
| Pair optimization | Template 4 | Based on optimization | Transform equation |
Document generated for NeetCode Practice Framework — API Kernel: monotonic_deque