Coding Interview Patterns Module 8 – Stacks and monotonic stacks
Next greater element with a monotonic stack
For every day, how long until a warmer one? Find the next greater element of each position in one pass with a monotonic stack, from brute force to proof.
What you will learn
- Find the next greater or next smaller element of every position in O(n) time
- Prove that the stack gives every position its right answer, and count its work
- Choose the strict or the non-strict comparison that matches the tie rule of a problem
- Extend the method to previous-greater, next-smaller and circular versions
- Test a monotonic stack against a brute force on random inputs with many ties
Before you start
What you’ll learn · Before you start
What you will learn
- Find the next greater or next smaller element of every position in O(n) time
- Prove that the stack gives every position its right answer, and count its work
- Choose the strict or the non-strict comparison that matches the tie rule of a problem
- Extend the method to previous-greater, next-smaller and circular versions
- Test a monotonic stack against a brute force on random inputs with many ties
Before you start
On this page
The next greater element: For a position in a list, the first value after it that is strictly larger. In 31, 29, 33 it is 33 for both 31 and 29, and the last value has none. of a position in a list is the first value after it that is strictly larger: in 31, 29, 33, it is 33 for both 31 and 29. Asked for every position at once, the question has a slow answer, scanning forward from each position, and a fast one: a monotonic stack: A stack whose values stay in order from bottom to top, because each new item first pops every item it settles. It finds the nearest earlier or later larger, or smaller, item for every item in one pass. More in “Stack patterns: nesting and undo”, which holds the positions still waiting for their answer, with values that never rise from the bottom to the top. Each new value answers the waiting positions it beats, newest first, and then waits itself, so a list of n values takes O(n) time where the scans take O(n²). The problem is widely known as daily temperatures: how many days until a warmer day? The same loop answers the previous greater, next smaller and circular versions, each with one change.
The problem: days until a warmer day
The problem
A weather station records the highest temperature of each day, in whole degrees Celsius: temps[0..n − 1]. For
every day i, report wait[i], the number of days from day i to the first later day that is strictly warmer,
or 0 when no later day is warmer. Limits: 1 ≤ n ≤ 100,000 and −50 ≤ temps[i] ≤ 60.
Three examples, which every program in this lesson runs:
[31, 29, 33, 30, 30, 35, 32]gives[2, 1, 3, 2, 1, 0, 0]. Day 3 waits past day 4, which is just as warm, not warmer.[40, 38, 36]gives[0, 0, 0]: the days only cool.[27, 27, 28]gives[2, 1, 0].
Each day points to its first warmer day; the dashed bars have none
Text description of the diagram
The chart shows the seven days of the first example as bars, from day 0 to day 6, with their highest temperatures in °C: 31, 29, 33, 30, 30, 35 and 32. Under each bar are the day's number and its answer, the wait. Arrows below the chart join each day to the first later day that is strictly warmer.
- Day 0 (31 °C) and day 1 (29 °C) both point to day 2 (33 °C): they wait 2 days and 1 day.
- Day 2 (33 °C), day 3 (30 °C) and day 4 (30 °C) all point to day 5 (35 °C): they wait 3, 2 and 1 days. Day 4 has the same temperature as day 3, which is not warmer, so day 3 waits past it.
- Day 5 (35 °C) and day 6 (32 °C) have no warmer day after them. Their bars are dashed and their answer is 0.
The arrows never cross: when one arrow lies over another, it covers it entirely. Several days can wait for the same warm day, and a day that is still waiting is never warmer than an earlier day that is still waiting.
The brute force: scan forward from every day
From each day, read the following days in order and stop at the first warmer one. It is plainly correct, so it is the judge for faster code, but a day with no warmer day after it reads every later day. The run counts comparisons on a log that cools steadily, where no day is warmer than an earlier one, and on random readings between 20 and 45 °C.
"""Days until a warmer day, the brute force: from every day, scan forward until a strictly warmer day."""
import random
def wait_for_warmer_brute(temps):
"""wait[i]: how many days after day i the first strictly warmer day comes (0 if none), and the comparisons made."""
n = len(temps)
wait = [0] * n
comparisons = 0
for i in range(n):
for j in range(i + 1, n):
comparisons += 1
if temps[j] > temps[i]:
wait[i] = j - i
break
return wait, comparisons
print(wait_for_warmer_brute([31, 29, 33, 30, 30, 35, 32])[0])
print(wait_for_warmer_brute([40, 38, 36])[0])
print(wait_for_warmer_brute([27, 27, 28])[0])
# A log that never warms up: it cools from 60 to -50 degrees and stays level in between.
for n in (1_000, 2_000):
cooling = [60 - 110 * i // n for i in range(n)]
print(f"cooling log of {n:,} days: {wait_for_warmer_brute(cooling)[1]:,} comparisons")
rng = random.Random(42)
for n in (5_000, 10_000):
readings = [rng.randint(20, 45) for _ in range(n)]
print(f"random log of {n:,} days (20 to 45 degrees): {wait_for_warmer_brute(readings)[1]:,} comparisons") Output
[2, 1, 3, 2, 1, 0, 0] [0, 0, 0] [2, 1, 0] cooling log of 1,000 days: 499,500 comparisons cooling log of 2,000 days: 1,999,000 comparisons random log of 5,000 days (20 to 45 degrees): 490,523 comparisons random log of 10,000 days (20 to 45 degrees): 1,971,665 comparisons
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 warmer_brute.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
Doubling the log quadruples the work in both cases. A cooling log costs exactly n(n − 1)/2 comparisons, 4,999,950,000 at the limit of 100,000 days: close to a minute of C++ even at 10⁸ simple steps a second, the planning figure of the lesson on constraints, where a second is allowed. Random readings are hardly kinder: the hottest days never meet a warmer day, so each reads to the end, and the same fourfold rule leads to about 2 × 10⁸ comparisons, two seconds at that rate.
Checkpoint
Check: What scanning forward from every day costs
What to notice
The scans repeat one another. Day 2 answers days 0 and 1 at once, yet the brute force found that out twice. Day 5 likewise answers days 2, 3 and 4, which the brute force found in three more scans. Now look at the days whose answer is still open: from the oldest to the newest they never get warmer, because a warmer newer day would already have answered the older one. So a new reading can only answer a run of the newest open days, which is the stack cue from the overview of this module.
Checkpoint
Check: The days still waiting for a warmer day
The questions below practise both ideas.
Practice that adapts to you
Practise this lesson
6 questions about 8 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
A stack of open days settles every day in one pass Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- A dry run on the first example
- The invariant, and the proof that every answer is right
- Counting the work: at most 2n pushes and pops, measured
- The code in 14 languages, and the empty-stack check each needs
- Ties, slips and the stress test that finds them
- Strict or not: the comparison is the tie rule
- Edge cases to test before you submit
- The same loop answers previous, smaller and circular versions
- Practise: the next busier server in a ring
- Find the next busier server in a round-robin ring
- Follow-up questions, answered
- Interview questions
- What does a monotonic stack keep, and why are the values on it in order?
- An interviewer points at the while loop inside your for loop and says your next-greater solution is O(n²). Show that it is O(n), and say which input makes the stack largest.
- Your next-greater code passes the sample tests, but a hidden test fails, and the inputs can repeat values. What do you check first, and which three small tests do you add?
- A sensor sends one reading a minute for a year, and each reading's wait for a higher one must be reported as soon as it is known. How much memory can the waiting readings take, and how would you bound it?
- Key takeaways
2 diagrams · 4 runnable examples (Python) · 1 exercise with sample tests · 3 interview answers · about 42 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Terms in this lesson
- next greater element
- For a position in a list, the first value after it that is strictly larger. In 31, 29, 33 it is 33 for both 31 and 29, and the last value has none.
References
- Optimal doubly logarithmic parallel algorithms based on finding all nearest smaller values (Journal of Algorithms 14(3)) (Journal of Algorithms (Berkman, Schieber and Vishkin))
- The Python Tutorial: Using Lists as Stacks (Python Software Foundation)
- Time complexity of operations on built-in types (Python Software Foundation)
- ECMAScript language specification: Array.prototype.pop (Ecma International (TC39))
- Array.prototype.at() (returns undefined outside the array) (MDN Web Docs (Mozilla))
- The Java Language Specification, Java SE 25: initial values of variables (4.12.5) (Oracle)
- The Java Language Specification, Java SE 25: boxing conversion (5.1.7) (Oracle)
- Java SE 25 API: ArrayDeque (Oracle)
- std::vector::back (cppreference.com)
- std::range_formatter (C++23) (cppreference.com)
- Stack<T>.TryPeek (returns false when the stack is empty) (Microsoft)
- PHP manual: arrays (the key false is stored under 0) (The PHP Group)
- PHP manual: end() (returns false for an empty array) (The PHP Group)
- The Rust Reference: chains of conditions (let chains) (The Rust Project)
- The Go Programming Language Specification: Slice expressions (The Go Authors)
- The Swift Programming Language: the basics (optional binding) (Swift.org)
- Ruby documentation: class Array (last returns nil when the array is empty) (Ruby (ruby-lang.org))
- Kotlin standard library: contentToString (JetBrains)
- Dart API: List.removeLast (the list must not be empty) (Dart (Google))
- AddressSanitizer (The Clang Team (LLVM Project))
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress