Pattern

Monotonic Stack

Find the next greater or smaller element in O(n) using a stack that maintains sorted order.

Roadmap

  1. What Is This Pattern?
  2. When to Use It
  3. Variant 1 — Monotonic Decreasing Stack
  4. Variant 2 — Monotonic Increasing Stack
  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 stack is a stack where the elements are always in increasing (or decreasing) order from bottom to top. When a new element would violate this order, we pop elements until the order is restored. The magic: each pop reveals the answer for the popped element.

The Core Idea

We scan the array left to right. The stack holds "unresolved" elements that are still waiting for their answer. When a new element causes pops, it becomes the answer for every popped element.

Decreasing stack: waiting for "next greater"
5
3
1
4
New element 4 arrives. It is greater than 1 and 3, so pop them both:
Pop 1 next greater of 1 = 4
Pop 3 next greater of 3 = 4
Stack after: [5, 4] — still decreasing.
5
3
1
💡

Why does this work? The stack holds elements that have not yet found their "next greater." The moment a larger element appears, it is by definition the next greater element for everything smaller sitting on top of the stack. We pop them because they are now resolved.

Step 02

When to Use It

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

"Next greater element" or "next smaller element"
The classic monotonic stack problem. Find the first element to the right that is larger (or smaller).
"Previous greater/smaller element"
Same idea, but scan from right to left, or use a slightly different approach.
"Stock span" or "daily temperatures"
How many consecutive days before was the price lower? How many days until a warmer temperature?
"Histogram" or "trapping rain water"
Need to process elements relative to their neighbors efficiently to find bounded regions.

The key clue: You need to find, for each element, the nearest element that is larger or smaller than it. A brute-force nested loop would be O(n²). The monotonic stack solves it in O(n) by cleverly processing elements in order and immediately resolving relationships as they appear.

Step 03

Variant 1 — Monotonic Decreasing Stack

Next Greater Element

The stack holds elements in decreasing order (from bottom to top). When a new element is larger than the top, we pop. Each popped element's "next greater" is the new element. This is the most common variant.

Walkthrough: [2, 1, 2, 4, 3]

Find the next greater element for each position. Result: [4, 2, 4, -1, -1]

i = 0, nums[0] = 2
2
1
2
4
3
Stack is empty. Push 2.
2
i = 1, nums[1] = 1
2
1
2
4
3
1 ≤ 2 (top). Order is maintained. Push 1.
2
1
i = 2, nums[2] = 2
2
1
2
4
3
2 > 1 (top). Pop 1result[1] = 2
2 ≤ 2 (new top). Stop popping. Push 2.
2
2
i = 3, nums[3] = 4
2
1
2
4
3
4 > 2 (top). Pop 2result[2] = 4
4 > 2 (new top). Pop 2result[0] = 4
Stack empty. Stop popping. Push 4.
4
i = 4, nums[4] = 3
2
1
2
4
3
3 ≤ 4 (top). Order is maintained. Push 3.
4
3
Done
Elements 4 and 3 remain on the stack. They never found a next greater element, so result[3] = -1 and result[4] = -1.
result:
4
2
4
-1
-1
4
3
Step 04

Variant 2 — Monotonic Increasing Stack

Next Smaller Element

The stack holds elements in increasing order (from bottom to top). When a new element is smaller than the top, we pop. Each popped element's "next smaller" is the new element. This is the mirror image of Variant 1.

Example: [3, 7, 8, 4]

Find the next smaller element for each position. Result: [-1, 4, 4, -1]

i=0: Push 3. Stack: [3]
i=1: 7 ≥ 3, push. Stack: [3, 7]
i=2: 8 ≥ 7, push. Stack: [3, 7, 8]
i=3: 4 < 8. Pop 8result[2] = 4
4 < 7. Pop 7result[1] = 4
4 ≥ 3. Push. Stack: [3, 4]
Done: 3 and 4 stay → result[0] = -1, result[3] = -1
result:
-1
4
4
-1
🎯

Choosing the variant: If you want the next greater element, use a decreasing stack (pop when new > top). If you want the next smaller element, use an increasing stack (pop when new < top). The invariant always matches what you are searching for.

Step 05

Template Code

Here is the annotated Java template for the "next greater element" variant. Adapt the comparison operator for "next smaller."

Next Greater Element

// Next Greater Element for each index
int[] nextGreater(int[] nums) {
    int n = nums.length;
    int[] result = new int[n];
    Arrays.fill(result, -1);               // default: no answer

    Deque<Integer> stack = new ArrayDeque<>(); // stores INDICES

    for (int i = 0; i < n; i++) {
        // Pop all elements smaller than current
        while (!stack.isEmpty() && nums[stack.peek()] < nums[i]) {
            result[stack.pop()] = nums[i]; // current is the answer
        }
        stack.push(i);                    // push current index
    }
    // Elements remaining on stack have no next greater (-1)
    return result;
}

Next Smaller Element (flip the comparison)

// Next Smaller Element: change < to >
while (!stack.isEmpty() && nums[stack.peek()] > nums[i]) {
    result[stack.pop()] = nums[i];
}
📝

Store indices, not values. The stack stores indices rather than values. This is critical because the result array is indexed by position. When we pop an index from the stack, we know exactly which position in the result array to fill.

Step 06

Visual Walkthrough

Let us trace through a larger example step by step, showing the stack and result array side by side at every stage.

Array: [4, 5, 2, 10, 8]

Finding next greater element for each position.

STEP ACTION STACK RESULT
i=0 (4) Push 0 [0:4] [-1, -1, -1, -1, -1]
i=1 (5) Pop 0res[0]=5, push 1 [1:5] 5, -1, -1, -1, -1
i=2 (2) 2 ≤ 5, push 2 [1:5, 2:2] 5, -1, -1, -1, -1
i=3 (10) Pop 2res[2]=10
Pop 1res[1]=10, push 3
[3:10] 5, 10, 10, -1, -1
i=4 (8) 8 ≤ 10, push 4 [3:10, 4:8] 5, 10, 10, -1, -1
result:
5
10
10
-1
-1
💡

Notice the pattern: Each element is pushed exactly once and popped at most once. That means the total number of push + pop operations across the entire loop is at most 2n, giving us O(n) total time, not O(n²).

Step 07

Common Problems

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

#496 Next Greater Element I Easy
#503 Next Greater Element II Medium
#739 Daily Temperatures Medium
#901 Online Stock Span Medium
#84 Largest Rectangle in Histogram Hard
#42 Trapping Rain Water Hard
💡

Practice tip: Start with #496 (direct application of the template) and #739 (store index differences instead of values). Once comfortable, tackle #84 which uses a monotonic stack in a more creative way: finding the nearest smaller bar on each side to compute rectangle widths.

Step 08

Interactive Demo

Enter an array and step through the monotonic stack algorithm. Watch the stack grow and shrink, and see the result array fill in as pops resolve elements.

⚙ Monotonic Stack Visualizer



ARRAY
RESULT
Press "Step →" or "Run All" to begin.
Step 09

Complexity Analysis

Time
O(n)
Space
O(n)

Why O(n) Time?

Even though there is a while loop inside the for loop, the total work is bounded. Each element is pushed exactly once and popped at most once. Over the entire algorithm, there are at most n pushes and at most n pops. Total operations: O(n + n) = O(n).

Space: The stack can hold at most n elements (when the array is already sorted in the desired order), plus the result array of size n. Total: O(n).

Amortized analysis: The inner while loop might run multiple times for a single iteration of i, but those pops can never happen again for those elements. Across all iterations of i, the total number of pops is bounded by n. This amortized cost is what makes the algorithm linear despite the nested loop.

Step 10

Key Takeaways

01

Push once, pop once = O(n). Each element is pushed onto the stack exactly once and popped at most once. This is the fundamental reason the algorithm runs in linear time, not quadratic time, despite the nested loop.

02

Decreasing stack for "next greater," increasing for "next smaller." The stack's order invariant directly corresponds to what you are searching for. Flip the comparison operator to switch between variants.

03

Store indices, not values. The stack holds indices so you can fill the result array at the correct position. You can always look up the value via the original array.

04

Unresolved elements stay on the stack. After processing all elements, anything left on the stack never found an answer. Their result is -1 (or whatever the default is).

05

Extends to circular arrays and histograms. For circular arrays (like #503), process the array twice by iterating with i % n. For histogram problems, the monotonic stack finds the nearest smaller bars on both sides, enabling O(n) area computation.