Pattern

Monotonic Queue

Track sliding window maximums or minimums in O(n) using a double-ended queue that maintains sorted order.

Roadmap

  1. What Is This Pattern?
  2. When to Use It
  3. Variant 1 — Sliding Window Maximum (Decreasing Deque)
  4. Variant 2 — Sliding Window Minimum (Increasing Deque)
  5. Template Code
  6. Visual Walkthrough
  7. Common Problems
  8. Interactive Demo
  9. Complexity Analysis
  10. Key Takeaways
Step 01

What Is This Pattern?

A monotonic queue (or monotonic deque) is a double-ended queue where the elements are always in increasing (or decreasing) order from front to back. When a new element would violate this order, we remove elements from the back until the order is restored. The front of the deque always holds the current answer.

The Core Idea

We maintain a deque of indices. Elements enter from the back and leave from both ends. The front holds the current window's extreme value (max or min). Elements that can never be the answer are discarded from the back before they even get a chance.

Decreasing deque: tracking window maximum
front
8
5
3
back
6
New element 6 arrives. It is greater than 3 and 5, so remove them from the back:
Remove 3 can never be the max while 6 exists
Remove 5 can never be the max while 6 exists
Deque after: [8, 6] — still decreasing. Front (8) is the window max.
deque
8
5
3
💡

Why does this work? If a newer element is larger than an older one in the deque, the older element can never be the window maximum — the newer element will outlive it in the window. So we discard the older element from the back. The front always holds the largest (and oldest) element currently in the window.

Step 02

When to Use It

Look for these recognition signals in the problem statement. If you spot one, a monotonic queue is very likely the intended approach.

"Sliding window maximum" or "sliding window minimum"
The classic monotonic queue problem. Find the max or min in every contiguous window of size k.
"Maximum/minimum in subarray" with constraints
When you need the extreme value within a bounded range and the range slides across the array.
"Shortest subarray with sum at least K"
Monotonic deque on prefix sums lets you find optimal left boundaries efficiently.
"DP with bounded lookback"
When a DP recurrence looks back at most k steps and needs the max/min of those states, the deque optimizes it from O(nk) to O(n).

The key clue: You need the maximum or minimum within a sliding window of fixed or variable size. A brute-force scan of each window would be O(nk). The monotonic queue solves it in O(n) by maintaining only the "useful" candidates in sorted order and removing stale elements from the front.

Step 03

Variant 1 — Sliding Window Maximum (Decreasing Deque)

Window Maximum

The deque holds indices whose values are in decreasing order (front to back). When a new element is larger than the back, we pop from the back. The front of the deque is always the index of the current window maximum. This is the most common variant.

Walkthrough: nums = [1, 3, -1, -3, 5, 3, 6, 7], k = 3

Find the maximum in every window of size 3. Result: [3, 3, 5, 5, 6, 7]

i = 0, nums[0] = 1
1
3
-1
-3
5
3
6
7
Deque is empty. Push 0. Window not full yet.
deque
1
i = 1, nums[1] = 3
1
3
-1
-3
5
3
6
7
3 > 1 (back). Remove 1 from back.
Deque empty. Push 1. Window not full yet.
deque
3
i = 2, nums[2] = -1
1
3
-1
-3
5
3
6
7
-1 ≤ 3 (back). Order maintained. Push 2.
Window [0..2] complete. max = nums[front] = 3
deque
3
-1
i = 3, nums[3] = -3
1
3
-1
-3
5
3
6
7
-3 ≤ -1 (back). Order maintained. Push 3.
Window [1..3]. Front index 1 is in range. max = 3
deque
3
-1
-3
i = 4, nums[4] = 5
1
3
-1
-3
5
3
6
7
5 > -3 (back). Remove -3.
5 > -1 (back). Remove -1.
5 > 3 (back). Remove 3.
Push 4. Window [2..4]. max = 5
deque
5
i = 5, nums[5] = 3
1
3
-1
-3
5
3
6
7
3 ≤ 5 (back). Order maintained. Push 5.
Window [3..5]. max = 5
deque
5
3
i = 6, nums[6] = 6
1
3
-1
-3
5
3
6
7
6 > 3 (back). Remove 3.
6 > 5 (back). Remove 5.
Push 6. Window [4..6]. max = 6
deque
6
i = 7, nums[7] = 7
1
3
-1
-3
5
3
6
7
7 > 6 (back). Remove 6.
Push 7. Window [5..7]. max = 7
deque
7
Done
All windows processed. The maximums are:
result:
3
3
5
5
6
7
deque
7
Step 04

Variant 2 — Sliding Window Minimum (Increasing Deque)

Window Minimum

The deque holds indices whose values are in increasing order (front to back). When a new element is smaller than the back, we pop from the back. The front always holds the current window minimum. This is the mirror image of Variant 1.

Example: nums = [3, 1, 4, 1, 5], k = 3

Find the minimum in every window of size 3. Result: [1, 1, 1]

i=0: Push 0. Deque: [3]
i=1: 1 < 3, remove 3 from back. Push 1. Deque: [1]
i=2: 4 ≥ 1, push. Deque: [1, 4]. Window [0..2]. min = 1
i=3: 1 < 4, remove 4. 1 ≥ 1, push. Deque: [1, 1]. Window [1..3]. min = 1
i=4: Front index 1 out of window [2..4], remove from front. 5 ≥ 1, push. Deque: [1, 5]. min = 1
result:
1
1
1
🎯

Choosing the variant: If you want the window maximum, use a decreasing deque (remove from back when new > back). If you want the window minimum, use an increasing deque (remove from back when new < back). The front always holds the answer.

Step 05

Template Code

Here is the annotated Java template for the sliding window maximum. Adapt the comparison operator for sliding window minimum.

Sliding Window Maximum

// Sliding Window Maximum using Monotonic Deque
int[] maxSlidingWindow(int[] nums, int k) {
    int n = nums.length;
    int[] result = new int[n - k + 1];
    Deque<Integer> dq = new ArrayDeque<>(); // stores INDICES

    for (int i = 0; i < n; i++) {
        // 1. Remove front if it's outside the window
        if (!dq.isEmpty() && dq.peekFirst() < i - k + 1) {
            dq.pollFirst();
        }
        // 2. Remove smaller elements from back
        while (!dq.isEmpty() && nums[dq.peekLast()] < nums[i]) {
            dq.pollLast();
        }
        // 3. Add current index to back
        dq.offerLast(i);
        // 4. Record answer once window is full
        if (i >= k - 1) {
            result[i - k + 1] = nums[dq.peekFirst()];
        }
    }
    return result;
}

Sliding Window Minimum (flip the comparison)

// Change < to > in the back-removal loop
while (!dq.isEmpty() && nums[dq.peekLast()] > nums[i]) {
    dq.pollLast();
}
📝

Two removal points. Unlike a monotonic stack (which only pops from one end), the deque removes elements from both ends: stale elements leave from the front (out of window), and dominated elements leave from the back (can never be the answer). This double-ended removal is what makes it a deque, not a stack.

Step 06

Visual Walkthrough

Let us trace through the classic example step by step, showing the deque contents, window boundaries, and output at every stage.

Array: [4, 3, 5, 4, 3, 3, 6, 7], k = 3

Finding the sliding window maximum for each window of size 3.

STEP ACTION DEQUE (vals) OUTPUT
i=0 (4) Push 0 [4]
i=1 (3) 3 ≤ 4, push 1 [4, 3]
i=2 (5) Remove 3, 4 from back, push 2 [5] 5
i=3 (4) 4 ≤ 5, push 3 [5, 4] 5, 5
i=4 (3) Front idx 2 out of [2..4]? No. 3 ≤ 4, push 4 [5, 4, 3] 5, 5, 5
i=5 (3) Front idx 2 < 3, remove front. 3 ≤ 4, push 5 [4, 3, 3] 5, 5, 5, 4
i=6 (6) Remove 3, 3, 4 from back, push 6 [6] 5, 5, 5, 4, 6
i=7 (7) Remove 6 from back, push 7 [7] 5, 5, 5, 4, 6, 7
result:
5
5
5
4
6
7
💡

Two types of removal: Notice at i=5, the front was removed because it fell outside the window (stale), while at i=6, elements were removed from the back because they were smaller (dominated). This dual removal — front for staleness, back for dominance — is the hallmark of the monotonic queue.

Step 07

Common Problems

These are the classic LeetCode problems that use the monotonic queue pattern, listed in rough order of difficulty.

#239 Sliding Window Maximum Hard
#862 Shortest Subarray with Sum at Least K Hard
#1425 Constrained Subsequence Sum Hard
#1438 Longest Continuous Subarray With Absolute Diff ≤ Limit Medium
#1499 Max Value of Equation Hard
#42 Trapping Rain Water Hard
💡

Practice tip: Start with #239 (the direct application of the template). Then try #1438 which uses two monotonic deques simultaneously — one for max and one for min. For an advanced challenge, #862 applies the deque to prefix sums with a variable-size window, which is a common pattern in competitive programming.

Step 08

Interactive Demo

Enter an array and window size, then step through the sliding window maximum algorithm. Watch the deque grow and shrink from both ends, and see the result array fill in as each window completes.

⚙ Monotonic Queue Visualizer



ARRAY
WINDOW MAXIMUMS
DEQUE
front
back
Press "Step →" or "Run All" to begin.
Step 09

Complexity Analysis

Time
O(n)
Space
O(k)

Why O(n) Time?

Each element is added to the deque exactly once (pushed to back) and removed at most once (from either end). Over the entire algorithm, the total additions and removals are at most 2n. Even though the inner while loop might run multiple times for a given i, the amortized cost across all iterations is O(n).

Space: The deque holds at most k elements at any time (the current window), so space is O(k). The output array of size n - k + 1 is additional but considered part of the output, not auxiliary space.

Comparison with Other Approaches

APPROACH TIME SPACE
Brute force O(nk) O(1)
Heap / TreeMap O(n log k) O(k)
Monotonic Deque O(n) O(k)

Amortized analysis: The inner while loop might remove multiple elements for a single i, but those elements were each added exactly once in a previous iteration. Across all iterations, total removals cannot exceed total additions (n). This amortized cost is what makes the algorithm linear despite the nested loop.

Step 10

Key Takeaways

01

Double-ended removal is the key insight. Unlike a monotonic stack (one end), the deque removes stale elements from the front and dominated elements from the back. This dual mechanism is what enables O(n) sliding window queries.

02

Decreasing deque for max, increasing for min. The deque's order invariant matches what you are looking for. For sliding window maximum, maintain decreasing order. For minimum, maintain increasing order. Flip the comparison to switch.

03

Store indices, not values. The deque stores indices so you can check whether the front element is still within the current window. You can always look up the value via the original array.

04

The front is always the answer. After processing each new element, the front of the deque holds the index of the maximum (or minimum) for the current window. This is guaranteed by the deque's order invariant combined with the front-staleness check.

05

Extends to DP optimization and variable windows. When a DP recurrence needs the best value from the last k states, a monotonic deque turns O(nk) into O(n). For variable-size windows (like #862), the deque works on prefix sums and removes from the front when optimality conditions are met rather than staleness.