0% completed
Introduction to Monotonic Stack
On This Page
Core idea
How it works
Recognize it when
The template
Variants in this chapter
Watch out for
How this compares with nearby patterns
Key Takeaways
You are given the daily temperatures for a week. For each day, say how many days you must wait before it gets warmer.
[73, 74, 75, 71, 69, 72, 76]
The direct approach takes each day and scans forward until it finds a warmer one. In the worst case, a list that only ever gets colder, every scan runs to the end. That is O(N²).
Now watch what happens on the day it reaches 72. Three days are still waiting for an answer: 75, 71 and 69. The 72 answers 71 and 69 at the same time, and it answers nothing else, because 75 is still larger.
The days still waiting are always in decreasing order. That is not a coincidence. A day stays unanswered only while every day after it was colder.
So keep those waiting days in a stack, and the stack sorts itself. When a new value arrives, pop everything it is larger than. Each pop is one answer. Each day is pushed once and popped once.
That turns O(N²) into O(N).
Core idea
A Monotonic Stack is a stack whose contents are kept in sorted order, either always increasing or always decreasing.
The order is maintained by popping before pushing. Anything that would break the order is removed first, and every removal is an answer to a question that was waiting.
How it works
For the next warmer day, the steps are:
- Start with an empty stack that will hold indices, not values.
- Walk the list from left to right.
- While the current value is greater than the value at the top index, pop that index. Record its answer as the distance between the two positions. Stop when the stack empties.
- Push the current index.
- Anything still on the stack at the end has no answer, because nothing larger ever arrived.
The inner while looks like it makes this quadratic, and it does not. Each index is pushed exactly once and popped at most once across the whole run. So the total work is O(N), and the space is O(N) for the stack.
Store indices rather than values. You almost always need the distance or the position, and you can read the value from the index whenever you want.
Recognize it when
Use a Monotonic Stack when the question shows these signs.
- You need the next or previous greater or smaller element, for every position.
- The wording is about waiting, spans, or how far until something happens.
- You are asked how much each element contributes across all subarrays, as a minimum or a maximum.
- You are removing characters or digits to make a result as small or as large as possible.
- Your first idea is for each element, scan forward until a condition holds.
It is not this pattern when:
- You need the maximum inside a fixed window that slides. That is a Monotonic Queue, which pops from both ends.
- Only the most recent item matters, with no ordering rule. That is a plain stack.
- The array is sorted already, which usually means Two Pointers or Binary Search.
The template
function nextGreater(values):
answer = array of "none", same length as values
stack = empty # holds indices, kept decreasing by value
for i from 0 to length(values) - 1:
while stack is not empty and values[i] > values[top of stack]:
j = pop from stack
answer[j] = i - j # or values[i], depending on the question
push i onto stack
return answer # anything left on the stack keeps "none"
Two decisions define the problem. The comparison operator sets the direction. Using > keeps the stack decreasing and finds the next greater element. Using < keeps it increasing and finds the next smaller one. The line inside the while decides what an answer looks like. That may be a distance, a value, or a running total.
Variants in this chapter
| Variant | What a pop means | Problems in this chapter |
|---|---|---|
| Next greater value | the popped item just found its answer | Next Greater Element, Daily Temperatures |
| Cancel out neighbours | the popped item and the new one destroy each other | Remove All Adjacent Duplicates In String, Remove All Adjacent Duplicates in String II |
| Drop items to improve the result | the popped item was making the answer worse | Remove K Digits, Remove Nodes From Linked List |
| Count a contribution | the popped item's range of influence is now known | Sum of Subarray Minimums |
The first row is the one to learn. The others change what happens during the pop, not the loop around it.
Watch out for
- Choosing the wrong direction. A decreasing stack answers "next greater" and an increasing stack answers "next smaller". Write down which one the question needs before you write the loop.
- Storing values instead of indices. Values alone cannot give you a distance, and duplicated values become impossible to tell apart.
- Forgetting the leftovers. Whatever remains on the stack at the end never found an answer, and those positions need a default such as zero or minus one.
- Getting duplicates wrong. With equal values,
>and>=give different results. Decide whether an equal value counts as an answer before choosing the operator. - Assuming the nested loop is quadratic. Every index is pushed once and popped once, so the whole scan stays linear.
How this compares with nearby patterns
| If the question asks for | Use |
|---|---|
| the next or previous greater or smaller element | Monotonic Stack |
| matching, nesting, or plain undo | Stack |
| the maximum or minimum inside a sliding window | Monotonic Queue |
| a contiguous range that grows and shrinks | Sliding Window |
The Monotonic Queue chapter later in this course is the same idea with one change. A queue can drop items from the front as well as the back, which is what lets it forget elements that have left the window.
Key Takeaways
- The stack holds items that are still waiting for an answer, and they are always in sorted order.
- Popping before pushing keeps that order, and every pop resolves one waiting item.
- Store indices, because distances and positions are usually what the question wants.
- The scan is
O(N), since each index is pushed once and popped once. - The comparison operator chooses the direction, and the body of the pop chooses the answer.
Let's apply it to the first problem, Remove Nodes From Linked List.
karrad
· 3 years ago
- Input:
nums1 = [9,7,1],nums2 = [1,7,9,5,4,3] - Output:
[-1,9,7]
In this example, we first take 9 and compare to every number to right of 9 in nums2. No number >9 so -1
Then 7. The 1st number to right of 7 (i.e only consider last 4 numbers in nums2) is 9
Now we consider 1. If we go by the same logic, we should only consider 5,4,3 in nums2. Why is the answer 7? Should it not be 5?
Thanks
M
Kinshuk Agrawal
· 2 years ago
It would be nice to have such a comprehensive explanation + pseudo code for every pattern
Anand Mohan
· 3 years ago
There seems to be a mistake in the image. We are using monotonically decreasing stack.
On This Page
Core idea
How it works
Recognize it when
The template
Variants in this chapter
Watch out for
How this compares with nearby patterns
Key Takeaways