AlgoMaster Logo

Introduction to Monotonic Stack

High Priority11 min readUpdated June 4, 2026
Listen to this chapter
Unlock Audio

Monotonic Stack is a pattern that shows up in LeetCode problems where you need to find the next greater or smaller element in an array.

With this pattern, you can reduce the time complexity of many array problems from O(n²) down to O(n).

This chapter covers:

  • The definition of a monotonic stack.
  • When to apply the pattern.
  • How to implement it in code.

What is a Monotonic Stack?

A Monotonic Stack is a stack whose elements stay in either increasing or decreasing order from bottom to top.

The stack maintains a monotonic order: at any moment, the values stored (from bottom to top) form an increasing or decreasing sequence. Before pushing the current element, pop all stack-top elements that violate the invariant.

In a Monotonic Increasing Stack, the values from bottom to top form an increasing sequence. Before pushing a new element, pop any top elements that violate the order.

2
5
8
10
Top
  • For example, if you have the numbers 2, 5, 8 in the stack, the next number you add must be greater than 8.

In a Monotonic Decreasing Stack, the values from bottom to top form a decreasing sequence. Before pushing a new element, pop any top elements that violate the order.

The chapter's example uses a strict comparison (>), which pops only elements strictly smaller than the current one and leaves equal elements in place. The choice between strict (>) and non-strict (>=) depends on the problem: Daily Temperatures uses strict comparison, while some histogram variants require non-strict.

9
6
4
1
Top
  • For example, if you have 9, 6, 4 in the stack, the next number must be smaller than 4.

In most problems, store the indices of the numbers in the stack rather than the actual values.

This gives you more flexibility when accessing values from the array by index.

How to Implement a Monotonic Stack?

Consider an example problem: Next Greater Element.

The task is to find the next greater element for each number in an array.

The "next greater element" is the first number to the right that is larger than the current number.

Consider the array:

0
2
1
1
2
5
3
6
4
2
5
3

We want to find the next greater element for each number:

  • For 2 and 1, the next greater element is 5.
  • For 5, it is 6.
  • For 6, there is no greater element to the right.
  • For 2, it is 3.
  • For 3, there is no next greater element because it is the last number.

A brute force solution would involve a nested loop comparing each element with elements to its right until a greater element is found.

But this would take O(n²) time in the worst case.

Using a monotonic stack, we can bring the time complexity down to O(n).

The approach works as follows:

  • We use a stack to store indices, and we maintain the corresponding values in decreasing order from bottom to top.
  • We also use a results list, initially filled with -1 (which will store the next greater element for each index).
  • As we go through the array:
    • If the current number is greater than the number at the top of the stack, we pop from the stack and update the result for that index.
    • We continue this process, then push the index of the current number onto the stack.

We use ArrayDeque instead of java.util.Stack. java.util.Stack is synchronized and considered legacy; ArrayDeque is the recommended stack implementation in modern Java.

Walk through an example.

The array is [2, 1, 5, 6, 2, 3].

We maintain the following:

  • Stack: to store indices.
  • Results: initialized to all -1 values, which is the default answer if no next greater element exists for an index.

Go through the array step by step:

  • At index 0 (2): The stack is empty, so we push index 0.
  • At index 1 (1): Since 1 is smaller than 2, we push index 1.
  • At index 2 (5): 5 is greater than 1. We pop index 1 and update result[1] = 5. 5 is also greater than 2, so we pop index 0 and update result[0] = 5. Then, we push index 2.
  • At index 3 (6): 6 is greater than 5. We pop index 2 and set result[2] = 6. Push index 3.
  • At index 4 (2): Since 2 is less than 6, we push index 4.
  • At index 5 (3): 3 is greater than 2. We pop index 4 and set result[4] = 3. Push index 5.

After processing all the numbers, the result array is [5, 5, 6, -1, 3, -1].

The algorithm finds the next greater element for each index in O(n) time.

Why It Is O(n) Despite the Nested While Loop

The presence of a while loop inside a for loop usually suggests O(n²) behavior, but this case is different. The total work is bounded by counting how many times each index can be pushed and popped.

Each index is pushed onto the stack exactly once, when the outer loop reaches it. Each index is popped at most once, because once it leaves the stack it never returns. Across the entire run of the outer loop, the total number of push operations is n, and the total number of pop operations is at most n.

The inner while loop only runs when it can pop something. Its cost across the full outer loop sums to at most n, not n per outer iteration. The combined work is n pushes plus at most n pops plus n outer iterations, which is O(n) in total. This counting argument is the standard amortized analysis used for monotonic stack problems.

Four Variants: Next/Previous Greater/Smaller

The next greater element template covered above is one of four canonical variants. The same stack idea handles all of them with small tweaks to the comparison and the moment the answer is recorded.

Scroll
GoalTraversal directionStack monotonic orderWhen to pop
Next Greater Element (right)left to rightdecreasing (top is smallest)pop while stack top < current
Previous Greater Element (left)left to rightdecreasingpop while stack top <= current; answer = new top before push
Next Smaller Element (right)left to rightincreasing (top is largest)pop while stack top > current
Previous Smaller Element (left)left to rightincreasingpop while stack top >= current; answer = new top before push

The two "right" variants record the answer at the moment of popping: the element that triggers the pop is the next greater (or smaller) for the popped index. The two "left" variants record the answer from the stack top before pushing the current index, since whatever survives the pop chain on top of the stack is the previous greater (or smaller).

Below is the Previous Smaller Element template in all eight languages, mirroring the structure of the Next Greater template above:

The next-greater template above demonstrates the right-side pattern: the answer is written when an index is popped. The previous-smaller template demonstrates the left-side pattern: the answer is read from the surviving top of the stack right before the current index is pushed. Once both patterns are familiar, the other two variants are obtained by flipping the comparison operator.

Strict vs Non-Strict Comparison

The choice between strict (>) and non-strict (>=) inside the while condition decides what happens to equal elements. With strict comparison, equal elements stay on the stack and become part of the answer chain. With non-strict comparison, equal elements pop each other and are merged.

The right choice depends on the problem:

  • Daily Temperatures (find next warmer day): use strict > so that identical temperatures do not count as warmer.
  • Largest Rectangle in Histogram: use >= when iterating so that bars of equal height get merged into one rectangle rather than being computed separately.
  • Sum of Subarray Minimums: use one strict and one non-strict (for example, strict on one side and non-strict on the other) to avoid double-counting subarrays where the minimum appears more than once.

The guideline is to decide based on whether equal elements should be treated as still satisfying the condition or no longer satisfying it. If two equal values should both count as valid answers, use strict comparison so the older one survives. If they should be collapsed into a single representative, use non-strict comparison so the older one is popped.

The Sentinel Trick

For problems like Largest Rectangle in Histogram or Sum of Subarray Minimums, every remaining element on the stack must still be processed when the outer loop ends. The standard trick is to append a sentinel value to the input that forces every remaining element to pop.

For histogram problems, append a 0 (smaller than any real bar height). For next-greater problems, append INT_MAX (larger than any real value). The sentinel triggers the pop chain one final time, draining the stack inside the main loop without a separate post-loop:

Without the sentinel, the loop body has to be duplicated after the loop ends to drain whatever remains on the stack. The sentinel removes that duplication and keeps the main logic in one place.

Monotonic Stack vs Monotonic Deque

A monotonic stack is LIFO. It works when the only boundary that matters is the most recent one, which covers next/previous greater/smaller problems.

A monotonic deque is a double-ended queue. It is needed when elements must also be evicted from the front, typically because of a sliding window constraint. The canonical example is Sliding Window Maximum (LC 239): the deque holds candidate indices in decreasing order of value, the front of the deque is the current window's maximum, and elements are evicted from the front when their index falls outside the window.

The mechanics of the back are identical between the two. Both structures pop from the back to maintain the monotonic invariant when a new element arrives. The only added rule for the deque is the front-side eviction driven by window bounds. A monotonic stack is what a monotonic deque becomes when no such eviction is ever needed.

Classic Problems

The same monotonic stack template, with small variations on comparison and what to record, solves a recurring family of problems:

  1. Daily Temperatures (LC 739): For each day, find the number of days until a warmer day. Use a next-greater-right stack of indices and record j - i (the index distance) when popping.
  2. Largest Rectangle in Histogram (LC 84): For each bar, find the largest rectangle in which it is the shortest bar. Previous-smaller and next-smaller boundaries determine the rectangle's width.
  3. Trapping Rain Water (LC 42): Maintain a stack of decreasing heights. Each pop corresponds to a valley whose trapped water is computed from the popped height and the two surrounding boundaries.
  4. Sum of Subarray Minimums (LC 907): For each element, count how many subarrays have it as the minimum, then sum those contributions. Previous-smaller-or-equal and next-smaller boundaries give the count without double-counting equal values.
  5. Stock Span (LC 901): For each day, count consecutive prior days where the price was less than or equal to today. Previous-greater pattern with index distances as the answer.
  6. Next Greater Element II (LC 503): Same as next greater, but the array is circular. Iterate twice through the array or use i % n indexing so each element gets a chance to see every other element on its right.

Why Store Indices Instead of Values

Storing indices on the stack rather than values keeps positional information available. Many problems need the distance between two positions, not just the comparison result. Daily Temperatures asks "how many days until warmer", which requires computing j - i when an element pops. Largest Rectangle needs both the boundary index (to compute width) and the bar height (to compute area).

As a default, store indices and read values through nums[stack.peek()] when comparing. The index gives access to both the position and the value; the reverse is not true.

Quiz

Introduction Quiz

10 quizzes