Monotonic Stacks: Next Greater Element
Answer 'next greater / smaller' for every element in one O(n) pass.
14 min, 0 of 3 activities solved
View cheatsheetGetting Python ready… examples can run in a moment.
The question: for every element, find the first element to its right that is bigger (-1 if none).
nums = [2, 1, 5, 3, 4]
answer = [5, 5, -1, 4, -1]
The brute force scans right from every index:
O(n²). Notice the waste: while scanning for 2 we
walk past 1 and 5, then scan for 1 and walk
past 5 all over again.
The idea: walk left to right and keep a stack of
indices that are still waiting for their answer.
When a new value x arrives, it is the answer for
every waiting element smaller than it - pop them all
and record x. Then x itself starts waiting.
i x pops (answer = x) stack (values)
0 2 - [2]
1 1 - [2, 1]
2 5 1 -> 5, 2 -> 5 [5]
3 3 - [5, 3]
4 4 3 -> 4 [5, 4]
end: 5 and 4 never answered -> -1
Look at the stack column: the values are always decreasing from bottom to top. If a bigger value sat above a smaller one, it would already have popped it. That ordering is why it's called a monotonic stack, and why we only ever need to look at the top.
Brute force vs monotonic stack
Change res[j] = x to res[j] = i - j and you get
"how many steps until something bigger" - that's
Daily Temperatures.
Why is it O(n) with a while inside a for?
Count the work per index instead of per loop: each
index is pushed exactly once and popped at most
once. So across the whole run there are at most
2n stack operations. That's an amortized
argument: one step might pop many items, but only
because earlier steps pushed them.
What does this print?
Variations - same loop, tweak one thing:
- Next smaller: pop while
nums[top] > x. The stack is now increasing bottom to top. - Previous greater / smaller: after popping, whatever is on top (if anything) is the answer for the current index.
- Equal values:
<vs<=decides whether a tie counts as "greater". Read the problem carefully. - Circular array: loop
ioverrange(2 * n)and usenums[i % n]. - Not just arrays: any "who blocks whom" scan, e.g. cars catching up, or bars in a histogram that limit a rectangle's height.
Previous smaller: read the top after popping
Here storing values is fine because we only report values. For the largest-rectangle problem you'd need the indices, to measure widths.
You scan left to right to find each element's next SMALLER element. Reading the stack's values from bottom to top, they are always...
Next smaller element
Write next_smaller(nums) that returns, for every
index, the first value to its right that is
strictly smaller, or -1 if there is none.
[4, 8, 5, 2, 25] -> [2, 5, 2, -1, -1]
Use a monotonic stack of indices so it runs in O(n).