Beginner: foundations · Stacks, queues and linked lists · topic 18 of 61 · 40 min
Monotonic stacks and queues
Explanation
The "next greater" family
Many problems ask, for every item, about the nearest item to its right (or left) that is bigger or smaller: the next warmer day, the next higher price, the previous taller building. Checking all later items for every item is O(n²). A monotonic stack answers all of them in one O(n) pass.
Monotonic means "always moving in one direction". A monotonic stack keeps its items sorted from bottom to top, for example in decreasing order. Before pushing a new item, it pops every item that would break the order. Those popped items have just met their answer.
Next greater element
Walk left to right and keep a stack of INDEXES whose next greater value is not known yet. Their values never increase from bottom to top, because a bigger arrival would already have popped them. When x arrives, every waiting index with a value smaller than x has found its next greater element: x. Pop those and record x, then push the current index. Whatever is left on the stack at the end has nothing greater to its right, so its answer is −1.
| i | x | Popped (their answer is x) | Stack after (values) |
|---|---|---|---|
| 0 | 2 | — | [2] |
| 1 | 1 | — | [2, 1] |
| 2 | 2 | index 1 (value 1) | [2, 2] |
| 3 | 4 | index 2, then index 0 | [4] |
| 4 | 3 | — | [4, 3] |
def next_greater(nums):
result = [-1] * len(nums)
stack = [] # indexes still waiting for an answer
for i, x in enumerate(nums):
while stack and nums[stack[-1]] < x:
result[stack.pop()] = x
stack.append(i)
return result
print(next_greater([2, 1, 2, 4, 3])) # Output: [4, 2, 4, -1, -1]Why it is O(n)
Each index is pushed once and popped at most once, so the inner while loop runs at most n times over the WHOLE walk, not n times per item. That makes it O(n) time, with O(n) space for the stack and the result. This is the same amortised argument as the variable sliding window in Module 3.
Daily temperatures and stock span
Daily temperatures asks, for each day, how many days you must wait for a warmer one (0 if never). It is the next greater element again, but when index j is popped at day i you record the distance i − j instead of the value.
Stock span asks, for each day, how many consecutive days up to and including today had a price less than or equal to today's. This looks to the LEFT. Keep a stack of (price, span) pairs with strictly decreasing prices. A new price pops every pair whose price is less than or equal to it and adds their spans to its own span of 1. The example below shows it in code.
Sliding window maximum with a monotonic deque
For every window of size k, report its maximum. Rescanning each window is O(n × k), and a heap gives O(n log n). A monotonic deque gives O(n). Keep a deque of indexes whose values decrease from front to back; the front is then always the maximum of the current window. For each index i:
- Pop from the BACK every index whose value is ≤ nums[i]. They can never be a window maximum again: nums[i] is at least as big and stays in the window longer.
- Append i at the back.
- If the front index has slid out of the window (front ≤ i − k), pop it from the FRONT.
- Once i ≥ k − 1 the first window is complete; from then on its maximum is nums[front].
| i | nums[i] | Deque after (values) | Window max |
|---|---|---|---|
| 0 | 1 | [1] | — |
| 1 | 3 | [3] | — |
| 2 | −1 | [3, −1] | 3 |
| 3 | −3 | [3, −1, −3] | 3 |
| 4 | 5 | [5] | 5 |
| 5 | 3 | [5, 3] | 5 |
| 6 | 6 | [6] | 6 |
| 7 | 7 | [7] | 7 |
The trace is for nums = [1, 3, −1, −3, 5, 3, 6, 7] with k = 3, giving [3, 3, 5, 5, 6, 7]. Every index enters and leaves the deque at most once: O(n) time and O(k) space.
Choosing the direction and the comparison
- Next greater: keep values decreasing and pop while top < x. Next smaller: keep values increasing and pop while top > x.
- Store indexes, not values, when you need distances or must know whether an item has left a window.
- With duplicates, decide between < and <=. For "strictly greater", equal values must keep waiting, so pop only on <.
Common mistakes
- Storing values instead of indexes, then being unable to compute distances. Fix: push indexes and read nums[index].
- The wrong comparison with duplicates. Fix: write down the rule ("strictly greater") and test [3, 3, 3].
- Forgetting the items still on the stack at the end. Fix: fill the result with the "no answer" value (−1 or 0) before the loop.
- In window maximum, forgetting to drop the expired front index. Fix: pop from the front when front ≤ i − k.
- Believing the nested while makes it O(n²). Fix: count pushes and pops; there are at most n of each.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Monotonic stacks and queues · 1/8
This lesson is about monotonic stacks and queues, which answer "what is the next bigger item?" for every item at once.
Prefer a video by a teacher? Find free videos on “monotonic stack next greater element daily temperatures sliding window maximum deque” on YouTube ↗
In real life: Stock span on a share price chart
Trading apps and stock-market data sites often describe how strong today's price is with its span: the number of consecutive days, ending today, on which the price was at or below today's close. Computing it naively looks back day by day; a monotonic stack does all days in one pass. The prices below are made up.
def stock_span(prices):
stack = [] # (price, span), prices strictly decreasing
spans = []
for p in prices:
span = 1
while stack and stack[-1][0] <= p:
span += stack.pop()[1]
stack.append((p, span))
spans.append(span)
return spans
print(stock_span([100, 80, 60, 70, 60, 75, 85]))Practice (0/3 done)
Write next_greater(nums) that returns a list where position i holds the first value to the RIGHT of i that is strictly greater than nums[i], or -1 if there is none. Use a monotonic stack of indexes: O(n) time. Example: next_greater([2, 1, 2, 4, 3]) returns [4, 2, 4, -1, -1].
temps[i] is the temperature on day i. Write daily_temperatures(temps) that returns a list where position i holds how many days after day i you must wait for a STRICTLY warmer day, or 0 if no warmer day comes. O(n) time with a monotonic stack. Example: daily_temperatures([73, 74, 75, 71, 69, 72, 76, 73]) returns [1, 1, 4, 2, 1, 1, 0, 0].
Write sliding_window_max(nums, k) that returns the maximum of every window of k consecutive numbers, from left to right. If nums is empty, k < 1 or k > len(nums), return []. Use a deque of indexes with decreasing values: O(n) time. Example: sliding_window_max([1, 3, -1, -3, 5, 3, 6, 7], 3) returns [3, 3, 5, 5, 6, 7].
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Linked listsStacks, queues and linked lists · same module
- StacksStacks, queues and linked lists · same module
- set, stack, queue, deque and priority_queueC++ Programming (modern C++ and DSA) · The Standard Template Library (STL)
- Stacks and queuesPython Programming · Algorithms, data structures and testing
- StacksC Programming (from zero to systems) · Data structures in C
- Binary search on the answerSorting and searching
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What is the next greater element of each item in [4, 5, 2, 25]?
Answer: [5, 25, 25, −1]
4 → 5, 5 → 25, 2 → 25, and nothing is greater than 25.
Why is the monotonic stack walk O(n) even with a while loop inside the for loop?
Answer: Each index is pushed once and popped at most once over the whole walk
Count the total work: n pushes and at most n pops.
For "next strictly greater", the stack top has value 7 and the new value is 7. What happens?
Answer: Nothing is popped; the new 7 is pushed and waits too
Equal is not greater, so the old 7 keeps waiting (pop only on <).
In sliding window maximum, why may an index be popped from the BACK when a bigger or equal value arrives?
Answer: It can never be a window maximum again, because the new value is at least as big and stays longer
Any window that contains the old index also contains the newer, larger value.
Why store INDEXES rather than values in daily temperatures?
Answer: The answer is a distance, i − j, which needs the index
You need to know where the waiting day was to count the days between.
Which container allows fast push and pop at BOTH ends and indexing with []?Related
Answer: std::deque
deque is a double-ended queue with random access.
Which order does a stack follow?Related
Answer: Last In, First Out
A stack is LIFO: the most recently added item is removed first.
From Stacks and queues
A circular queue has capacity 4, head = 3 and count = 2. Which slot does the next enqueue use?Related
Answer: 1
(3 + 2) % 4 = 1. The items sit in slots 3 and 0; the next one goes in slot 1.
From Queues and deques
The window has size k and nums[i] just entered. Which item leaves?Related
Answer: nums[i − k]
The new window is i − k + 1 .. i, so the item just before it, nums[i − k], leaves.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Stacks, queues and linked lists” before its exam: Linked lists, Fast and slow pointers and merging lists.
- Module 4 exam: Stacks, queues and linked lists · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Recursion and backtracking. 61 topics left in the course before the final exam.