Skip to content
DSA stacks queues 8 min read

Monotonic Stack: The Next Greater Element Pattern

A monotonic stack is a stack whose elements are kept in sorted order (always increasing or always decreasing) as you push. Before pushing a new value, you pop everything that violates the order. This simple discipline solves a whole class of “find the next/previous greater or smaller element” problems in O(n) — replacing the obvious O(n²) double loop. It is one of the highest-leverage patterns in interviews.

The core problem: Next Greater Element

Given an array, for each element find the next element to its right that is larger; if none exists, report -1. Brute force scans right from every index: O(n²). The monotonic stack does it in one pass.

Key insight: scan left to right keeping a stack of indices whose answers are still unknown, with their values in decreasing order. When the current value is larger than the value at the top index, that current value is the “next greater” for the top — so pop it and record the answer. Repeat until the top is bigger, then push the current index.

#include <vector>
#include <stack>
std::vector<int> nextGreater(const std::vector<int>& a) {
    int n = a.size();
    std::vector<int> res(n, -1);
    std::stack<int> st;            // holds indices, values decreasing
    for (int i = 0; i < n; i++) {
        while (!st.empty() && a[i] > a[st.top()]) {
            res[st.top()] = a[i];
            st.pop();
        }
        st.push(i);
    }
    return res;
}

Complexity: O(n) time, O(n) space. Each index is pushed once and popped at most once — that’s the amortized O(n) argument, even though there’s a nested while.

Why O(n) despite a nested loop? Total pops across the whole run can’t exceed total pushes (n). The inner while does a lot of work occasionally, but never more than n times overall.

Reading the recipe

Memorize this skeleton; the four knobs are all you change per problem:

1. Iterate left→right (or right→left, depending on "next" vs "previous").
2. Keep a stack of INDICES.
3. While the new element breaks the desired order, pop and resolve those.
4. Push the new index.

Knobs:
 - direction (next vs previous)  → iterate forward vs backward
 - greater vs smaller            → flip the comparison (>  vs  <)
 - strict vs non-strict (> vs >=) → ties
 - store value or index/distance → what you record on pop

Variant: Daily Temperatures (distance, not value)

Same machinery, but record how many days until a warmer temperature — the index distance instead of the value.

#include <vector>
#include <stack>
std::vector<int> dailyTemperatures(const std::vector<int>& t) {
    int n = t.size();
    std::vector<int> res(n, 0);
    std::stack<int> st;
    for (int i = 0; i < n; i++) {
        while (!st.empty() && t[i] > t[st.top()]) {
            res[st.top()] = i - st.top();
            st.pop();
        }
        st.push(i);
    }
    return res;
}

Complexity: O(n) time, O(n) space.

When to reach for a monotonic stack

The signal is: “for each element, find the nearest element to one side that is greater/smaller.” That covers Next/Previous Greater/Smaller Element, Daily Temperatures, Stock Span, Largest Rectangle in a Histogram, and Trapping Rain Water. Its sliding-window sibling, the monotonic deque, extends the idea to window max/min (see deque).

Pitfall: Mixing up indices and values on the stack. Store indices when you need distances or to index back into the array; store values only when the value alone is the answer. Pick one and stay consistent.

Ready to apply it? The stacks & queues exercises include monotonic-stack problems.

Last updated June 25, 2026
Was this helpful?