Monotonic Stack
Maintain a stack whose values stay sorted so that every element finds its nearest greater or smaller neighbour in one O(n) pass, the engine behind span, histogram and visibility problems.
Instinct Each element needs its nearest bigger or smaller neighbour (next warmer day, span, widest bar) - keep a sorted stack of waiting indices.
Memory hack Tall people block the view - a newcomer kicks out everyone shorter waiting behind, and each kicked-out person just found their answer.
At a glance
When to use it
- Each element needs its nearest greater or smaller neighbour
- The answer is the distance to the next warmer, taller or cheaper item
- A bar's reach ends at the first shorter bar on either side (histograms, rectangles)
- You count how many items each element can see or span
Key idea
Keep a stack of waiting indices whose values stay sorted; a newcomer pops every weaker index and becomes its answer, and since each index is pushed and popped once the scan is O(n) instead of the O(n^2) look-right brute force.
Typical complexity
O(n) time, O(n) space
| # | Common problems | Level | Our solution | Practice |
|---|---|---|---|---|
| 1 | Next Greater Element I | Easy | Read solution | LeetCode ↗ |
| 2 | Daily Temperatures | Medium | Read solution | LeetCode ↗ |
| 3 | Online Stock Span | Medium | Read solution | LeetCode ↗ |
| 4 | 132 Pattern | Medium | Read solution | LeetCode ↗ |
| 5 | Largest Rectangle in Histogram | Hard | Read solution | LeetCode ↗ |
| 6 | Trapping Rain Water | Hard | Read solution | LeetCode ↗ |
The instinct
The brute force for "next greater element" checks every element to the right, giving O(n²). The monotonic stack notices that once a taller element arrives, every shorter element still waiting to its left has found its answer and can leave. What stays behind is always sorted - a staircase of candidates. Each index is pushed once and popped once, so the whole scan is O(n), even with a while loop inside the for.
Recognise it
| Signal in the problem | Why it points here |
|---|---|
| "next greater / next smaller element" | Literally the textbook use - one pass with a sorted stack. |
| "how many days until a warmer temperature" | Next greater, but return the index distance. |
| "span: consecutive previous days with price <= today" | Previous greater; popped items' spans are absorbed into the current one. |
| "largest rectangle in a histogram" / maximal rectangle | Each bar extends until the nearest shorter bar on each side. |
| "how many people can each person see to the right" | Count pops (people blocked) plus one if someone taller remains. |
| "find a 132 pattern" / i < j < k with a specific order | Scan from the right, keep a decreasing stack, track the best "2" value popped. |
| "trapping rain water", "sum of subarray minimums" | Each element's contribution is bounded by its nearest smaller/greater neighbours. |
| "remove k digits to get the smallest number" | Greedy: pop while the top is bigger than the incoming digit. |
Not this pattern if...
- You need the max/min of every fixed-size window - that is a monotonic deque (elements also expire from the front).
- You need the k-th largest, or values far away rather than the nearest qualifying one - think heap or sorting.
- Matching brackets or evaluating expressions - that is the plain stack pattern; nothing needs to stay sorted.
Memory hack
"Tall people block the view." Picture people lined up facing right. When someone tall walks in, everyone shorter in front of them in the queue can now see who blocks them - that tall newcomer is their next greater element - so they leave. The people still standing are always in decreasing height.
Rule of thumb for which way the stack is sorted:
- Looking for the next greater -> keep a decreasing stack (pop smaller ones).
- Looking for the next smaller -> keep an increasing stack (pop bigger ones).
- "Popped elements get their answer from the newcomer; the newcomer gets its previous answer from whatever is left on top."
Say it out loud in the interview:
- "Do I need greater or smaller, and to the left or right?"
- "So my stack is increasing/decreasing, storing indices so I can measure distances."
- "When I pop, I record the popped index's answer; after the loop, leftovers get the default."
How it works
Iterate left to right. Before pushing index i, pop every index whose value loses to a[i]; for each popped index, i is its next greater (or smaller) element. Then push i. Whatever is still on the stack at the end has no answer.
Trace for Daily Temperatures, [73, 74, 75, 71, 69, 72, 76] (decreasing stack of indices):
| i | temp | Pops (answer = i - j) | Stack after (temps) |
|---|---|---|---|
| 0 | 73 | - | 73 |
| 1 | 74 | 0 -> 1 | 74 |
| 2 | 75 | 1 -> 1 | 75 |
| 3 | 71 | - | 75 71 |
| 4 | 69 | - | 75 71 69 |
| 5 | 72 | 4 -> 1, 3 -> 2 | 75 72 |
| 6 | 76 | 5 -> 1, 2 -> 4 | 76 |
Index 6 stays on the stack, so its answer is 0.
For Largest Rectangle in Histogram, use an increasing stack. When bar j is popped by a shorter bar at i, i is its right limit and the new stack top is its left limit, so its widest rectangle has width i - top - 1. Appending a sentinel bar of height 0 flushes everything at the end.
flowchart TD
A[For each index i] --> B{"Stack not empty and a[top] loses to a[i]?"}
B -- yes --> C[Pop j, answer for j comes from i]
C --> B
B -- no --> D[Previous answer for i is the current top, if any]
D --> E[Push i]
E --> A
Java template
import java.util.*; class MonotonicStack { // Template 1: next greater element to the right (index distance; 0 if none). static int[] nextGreaterDistance(int[] a) { int n = a.length; int[] ans = new int[n]; Deque<Integer> st = new ArrayDeque<>(); // indices, values strictly decreasing for (int i = 0; i < n; i++) { while (!st.isEmpty() && a[st.peek()] < a[i]) { // flip to > for next smaller int j = st.pop(); ans[j] = i - j; // or a[i] for the value itself } st.push(i); } return ans; // leftovers keep the default 0 } // Template 2: increasing stack + sentinel for "widest span under each bar". static int largestRectangle(int[] h) { int n = h.length, best = 0; Deque<Integer> st = new ArrayDeque<>(); for (int i = 0; i <= n; i++) { int cur = (i == n) ? 0 : h[i]; // sentinel flushes the stack while (!st.isEmpty() && h[st.peek()] >= cur) { int height = h[st.pop()]; int left = st.isEmpty() ? -1 : st.peek(); // nearest smaller on the left best = Math.max(best, height * (i - left - 1)); } st.push(i); } return best; } // Template 3: online previous-greater with aggregated counts (stock span). static class Spanner { private final Deque<int[]> st = new ArrayDeque<>(); // {price, span} int next(int price) { int span = 1; while (!st.isEmpty() && st.peek()[0] <= price) span += st.pop()[1]; st.push(new int[]{price, span}); return span; } } }
Complexity
| Problem shape | Time | Space |
|---|---|---|
| Next/previous greater or smaller for all n | O(n) amortised | O(n) |
| Largest Rectangle in Histogram | O(n) | O(n) |
| Online Stock Span | O(1) amortised per call | O(n) |
| Circular next greater (scan 2n) | O(n) | O(n) |
The nested while looks quadratic, but every index enters and leaves the stack at most once, so total pops <= n.
Variations
- Next greater, value or distance - Next Greater Element I (with a hash map for the lookup array), Daily Temperatures.
- Previous greater with aggregation - merge popped spans into the current one: Online Stock Span.
- Nearest smaller on both sides - width of the bar's reach: Largest Rectangle in Histogram, Maximal Rectangle.
- Count what you pop - pops are people you can see: Number of Visible People in a Queue (scan from the right).
- Right-to-left with a tracked threshold - popped values become the best "middle" candidate: 132 Pattern.
- Circular arrays - loop
ifrom 0 to 2n - 1 and usei % n: Next Greater Element II. - Greedy lexicographic removal - Remove K Digits, Remove Duplicate Letters.
- Contribution counting - each element is the min of
left * rightsubarrays: Sum of Subarray Minimums.
Common mistakes
Mixing up strict and non-strict comparison. < versus <= decides how equal values are treated: Online Stock Span must pop equal prices, and contribution-counting problems must use strict on one side and non-strict on the other to avoid double counting.
- Storing values instead of indices, then being unable to compute distances or widths.
- Choosing the wrong monotonic direction - write down "next greater = decreasing stack" before coding.
- Forgetting leftovers: elements still on the stack need a default answer (-1, 0, or a width that reaches the end).
- In the histogram, computing width as
i - jinstead ofi - left - 1, whereleftis the new top after the pop. - Using a fixed-window max problem with a stack - when old elements expire from the front, you need a deque.
All practice problems
6 problems on this site use this pattern, easiest first. Solve them in order and say the instinct out loud before you code.