DSA · Stacks · Pattern
The Monotonic Stack
Finding the next greater element looks like it needs a nested loop. Keep the stack ordered while you scan and the whole thing collapses into one pass — every index goes on once and comes off once.
Step through 2 1 5 6 2 3 →01 The idea
Throw away what can never be the answer
You are given an array. For every position you want the first value to its right that is bigger. The obvious plan is to stand on each position and walk right until you find one. That walk re-reads the same values again and again.
Now scan left to right instead, and keep a pile of positions that are still waiting for an answer. The moment you pick up a value bigger than the top of that pile, that value is the answer for the top — and for everything under it that is also smaller. So you pop, answer, pop, answer. What survives on the pile never increases from bottom to top — strictly decreasing when the values are all different, flat where two of them tie. That ordering is why it is called a monotonic stack.
02 Hand trace
2 1 5 6 2 3, one index at a time
Six values: 2 1 5 6 2 3. The stack holds indices, written here as the values they point at with the top on the right. Follow index 0 — it is pushed in round 0, sits untouched through round 1, and is finally answered by 5 in round 2. Green notes are steps that settled an answer; red notes are steps that settled nothing.
Index 1 landed on top of index 0 at the end of round 1, and neither of them was touched again until round 2, when 5 cleared both in one move. Nobody re-read anything: seven value comparisons settled all six answers. The code in section 03 is this trace written down.
03 Naive vs improved
Two loops, or one loop and a stack
Both return the same array. The left one re-reads the tail for every position. The right one reads each position twice — once on the way onto the stack, once on the way off.
Brute force · O(n²)
for i = 0 to n-1: ans[i] = -1 for j = i+1 to n-1: if a[j] > a[i]: ans[i] = a[j]; breakreturn ans
Monotonic stack · O(n)
stack = empty # indices; top = last pushedfor i = 0 to n-1: while stack not empty and a[top] < a[i]: ans[pop()] = a[i] push ifor each i left on stack: ans[i] = -1return ans
The inner loop is gone. Line 3 on the left restarts at i+1 every time, so the same tail is read over and over. The stack already holds every position still waiting for an answer, in the exact order they will be answered, so there is nothing left to re-scan.
Why the stack stays ordered. Line 3 on the right removes everything smaller than a[i] before line 5 pushes i, so a[i] is never bigger than the value below it. That invariant is what lets you stop at the first value that does not pop — everything deeper is at least as big.
Strictly smaller, not smaller-or-equal. Using < on line 3 leaves equal values sitting on the stack, so an equal value is never reported as "greater". Loosen it to <= and you get the next greater-or-equal element instead. Interviewers toggle this one operator on purpose.
Line 6 is the −1 case. Anything still on the stack when the scan ends was never beaten by anything to its right. In the trace that was index 3 (value 6) and index 5 (value 3).
05 Cheat sheet
The numbers you will be asked for
| Case | Cost | Why |
|---|---|---|
| Time · any input | O(n) | Each index is pushed once and popped at most once — at most 2n stack operations in the whole run. |
| Comparisons · worst | ≤ 2n − 1 | Every value comparison either pops something (at most n) or ends one while-loop (at most n − 1). |
| Extra space · best | O(1) | A strictly increasing array pops the single waiting index before every push, so the stack never holds more than one. |
| Extra space · worst | O(n) | A strictly decreasing array never pops, so all n indices sit on the stack until the scan ends. |
| Brute force · worst | O(n²) | On 6 5 4 3 2 1 position i scans all n − 1 − i cells and still finds nothing: 15 comparisons for n = 6. |
06 Where & why
When the stack is the right tool
A monotonic stack is not a general speed-up. It pays off for exactly one shape of question: for every element, find the nearest element on one side that beats it under some comparison. Outside that shape it buys you nothing.
Reach for it when…
Use something else when…
| Situation | Better choice | Why |
|---|---|---|
| You want the largest value to the right, not the next one | Suffix maximum | One backward pass with a running maximum answers it. No stack, no popping rule, no invariant to defend. |
| You need the maximum over arbitrary query ranges | Sparse table or segment tree | A stack answers one fixed question per index. Arbitrary ranges need a structure built for queries. |
| You need how many elements to the right are greater | Fenwick tree over sorted values | The stack throws away everything it pops, so it can report the nearest but never a count. |
| The values arrive one at a time and you need answers immediately | Previous-side variant | Next greater needs the future. Rewrite the question as a previous-greater query, which a stack can answer online. |
07 Interview questions
Answer these out loud
Each answer is about twenty seconds spoken. Say the direct answer first, then the reason.
What is a monotonic stack?
Walk me through next greater element using one.
That is a while loop inside a for loop. Why is it not O(n²)?
What invariant does the stack keep, and why is the answer correct?
What is the worst case for space?
Why push indices instead of values?
Make it return the next greater-or-equal element instead.
How would you get the previous smaller element?
What if the array is circular?
When is the brute force actually fine?
Where would you use this outside a puzzle?
08 Practice problems
Six problems, one pattern
Every one of these is the same loop with a different thing recorded on the pop. Solve them in order; the twist is the part worth thinking about.