DSA sheet · Stack · Monotonic stack pattern
Daily Temperatures
This is the third monotonic stack problem. Under a story about weather, it's the next greater element to the right again. The twist: we must answer "how many days later", not "which temperature". That forces one important change: the stack stores indices, not values. The teacher also shows that the same problem can be solved walking left to right, and explains exactly what changes when you do that.
Every part below follows the same order:
① the question in simple words → ② what the constraints tell us → ③ intuition → ④ building the logic from examples → ⑤ approach steps → ⑥ code → ⑦ code line by line → ⑧ dry run → ⑨ complexity & remember
- Part 0 · Stacks, monotonic stacks and the 4 standard problems
- Part A · Brute force: two loops
- Part B · Optimal: stack of indices, walking from the right
- Part C · The same stack walking from the left
- Part D · Revision page
Part 0 · Before starting
Stack basics
A stack is a pile you can only use from the top: LIFO (Last In, First Out). Python's list works as a stack:
| action | Python | note |
|---|---|---|
| push x on top | st.append(x) | O(1) |
| pop the top | st.pop() | O(1), crashes if empty |
| peek at the top | st[-1] | O(1), crashes if empty |
| is it empty? | not st | check this before pop / peek |
Drawings on this page: left = bottom, right = top.
Monotonic stack in one paragraph
A monotonic stack keeps its items in sorted order from bottom to top. Before pushing a new item, we pop every item on top that would break the order. For "next greater" questions we pop the items that are not greater than the new one. A popped item is hidden behind a closer item that is at least as big, so it can never be the "first bigger one" for anything that comes later. Every item is pushed once and popped at most once, which is why the whole thing is O(n).
The 4 standard problems (the teacher lists them here)
If you know stacks, you know these four. Almost every monotonic-stack question is one of them in disguise:
| name | looking for | usual loop direction | pop while top is… |
|---|---|---|---|
| next greater to the right | first bigger value after i | right → left | ≤ current |
| next greater to the left | first bigger value before i | left → right | ≤ current |
| next smaller to the right | first smaller value after i | right → left | ≥ current |
| next smaller to the left | first smaller value before i | left → right | ≥ current |
Translating this question: "warmer" means greater. "Days you have to wait" means the future, so to the right. (If the question asked about a warmer day in the past, it would be next greater to the left.) So Daily Temperatures = next greater to the right, but report the distance.
Part A · Brute force: two loops
LeetCode 739
1The question in simple words
You get the temperature for each day. For every day i, return how many days you must wait until a strictly warmer day. If no warmer day ever comes, return 0 for that day.
- Day 0 (73): the very next day is 74, warmer → wait 1.
- Day 1 (74): next day 75 → 1.
- Day 2 (75): 71, 69, 72 are all colder. Day 6 (76) is the first warmer day → wait 6 − 2 = 4.
- Day 6 (76): no warmer day after it → 0. Day 7 (73): it's the last day → 0.
Key observation: the wait is just the difference of indices, j − i, where j is the first warmer day after i.
2What the constraints tell us
- Length from 1 to 10⁵. Temperatures between 30 and 100.
- The teacher's reason for reading constraints: they tell you in advance whether the brute force will pass. Brute force here is O(n²) = 10⁵ × 10⁵ = 10¹⁰ steps. Past ~10⁸ it's unsafe, and from 10⁹ it's a sure TLE.
- So we write the brute force to understand the problem, then optimise to O(n).
- Length ≥ 1, so the input is never empty (our code still handles an empty list).
3Intuition
Stand on day i. Walk forward day by day until it's warmer. Count the steps. That count is the answer.
4Building the logic
→ 0 is the answer when no warmer day exists. Pre-filling means we only overwrite when we find one. The last day never gets overwritten, and that's correct.
j starts at i + 1, right?→ Yes. A day can't be warmer than itself, and we only care about days after i.
> or >=?→ Strictly
>. "Warmer" means hotter. The same temperature is not warmer.ans[i] or ans[j]?→
ans[i]. We're searching on behalf of day i. Day j is just the day that ended the search.→ For the last day,
j = i + 1 is already past the end, so there's nothing to check. Its 0 is already there. (The teacher notes that running i to the very end also works, because the inner loop just doesn't run.)And once a warmer day is found, break. We want the first one, so going further would only risk overwriting it with a later, wrong day.
5Approach steps
ans = [0] * n.- For i from 0 to n−2, for j from i+1 to n−1:
- If
temps[j] > temps[i]:ans[i] = j − i, break. - Return ans.
6Code (Python)
class Solution:
def dailyTemperatures(self, temperatures):
n = len(temperatures)
ans = [0] * n # 0 = no warmer day
for i in range(n - 1):
for j in range(i + 1, n): # only future days
if temperatures[j] > temperatures[i]: # strictly warmer
ans[i] = j - i # days waited = index gap
break # first warmer day only
return ans7Code line by line
| line | what it means |
|---|---|
| ans = [0] * n | Default "never gets warmer". |
| for i in range(n - 1): | The day we're answering for. |
| for j in range(i + 1, n): | Scan the future, nearest day first. |
| if temperatures[j] > temperatures[i]: | Found a strictly warmer day. |
| ans[i] = j - i break | Store the gap at i, and stop scanning. |
8Dry run (the interesting row, i = 2)
| j | temp[j] | > 75? | action |
|---|---|---|---|
| 3 | 71 | no | keep going |
| 4 | 69 | no | keep going |
| 5 | 72 | no | keep going |
| 6 | 76 | yes | ans[2] = 6 − 2 = 4, break |
The other rows find their warmer day in one or two steps. Final [1, 1, 4, 2, 1, 1, 0, 0] ✓.
9Complexity & remember
- Time O(n²). The worst case is temperatures that never rise (e.g. 100, 99, 98, …): every inner loop scans to the end. At n = 10⁵ that's ~10¹⁰ → TLE (it does TLE when submitted).
- Space O(1) extra.
j runs forward just to find a greater value. The teacher's rule: whenever you go forward or backward again and again from each index to find a greater or smaller element, you can safely reach for a stack.Part B · Optimal: stack of indices, walking from the right
1The question again
Same question. Target: O(n), with one loop and a stack.
2What the constraints tell us
n ≤ 10⁵ → O(n) is about 2·10⁵ stack operations, which is instant. O(n) extra space for the stack is allowed.
3Intuition
Since the answer lives in the future (to the right), walk from the last day backwards, exactly like Next Greater Element. When we're at day i, every later day has already been seen and summarised in the stack: the stack holds the days that could still be "the next warmer day" for someone, with the nearest on top.
The one new idea: in Next Greater Element we pushed values. Here we need how far away the warmer day is, so we push the index of the day. From an index we can get both things we need:
- its temperature:
temperatures[st[-1]], used for comparing; - its distance:
st[-1] - i, used for the answer.
4Building the logic from the example (as the teacher walks it)
- Day 7 (73): the stack is empty, so no future day → 0. Push index 7.
- Day 6 (76): top is day 7 (73). Is 73 warmer than 76? No → pop it. The stack is empty → 0. Push 6.
- Day 5 (72): top is day 6 (76), warmer → answer 6 − 5 = 1. We don't search deeper: the top is the nearest candidate. Push 5.
- Day 4 (69): top is day 5 (72), warmer → 5 − 4 = 1. Push 4.
- Day 3 (71): top is day 4 (69), colder → pop. Why is it safe to throw 69 away? Every day to the left of day 3 sees 71 before it sees 69, and 71 is hotter. So if 69 were warm enough for that day, 71 is too, and it comes first. 69 can never again be anyone's "next warmer day". Next top is day 5 (72), warmer → 5 − 3 = 2. Push 3.
- Day 2 (75): top day 3 (71), colder → pop. Top day 5 (72), colder → pop (same reason: 75 now stands in front of both). Top day 6 (76), warmer → 6 − 2 = 4. Push 2.
- Day 1 (74): top day 2 (75) → 1. Push 1.
- Day 0 (73): top day 1 (74) → 1. Push 0.
k is popped while we stand at day i (i < k) because temp[k] ≤ temp[i]. Any day z we handle later is to the left of i. Walking forward from z, you reach i before k, and i is at least as warm. So whenever k would be warmer than z, so is i, and i comes first. Day k is permanently shadowed.→ An equal day is not warmer, so it can't answer day i. It's also shadowed for the days on the left: day i is just as warm and closer. So the condition is
temperatures[st[-1]] <= t.while inside the for O(n²)? At day 2 we popped twice.→ No. The pops are paid for once in total, not once per day. Each index is pushed exactly once and popped at most once over the whole run. The teacher's point: once a day is popped, it never comes back into the stack to be popped again. So all the while-loop runs together do at most n pops. See step 9.
→ Reading the indices' temperatures from bottom to top, they are strictly decreasing, e.g. after day 2:
[6, 2] → 76, 75. The top is the nearest future day, and each day lower down is further away and hotter.5Approach steps
ans = [0] * n, empty stack (of indices).- For i from n−1 down to 0, with
t = temperatures[i]: - While the stack is not empty and
temperatures[top] ≤ t→ pop. - If the stack is not empty →
ans[i] = top − i. - Push i.
- Return ans.
6Code (Python)
class Solution:
def dailyTemperatures(self, temperatures):
n = len(temperatures)
ans = [0] * n
st = [] # indices of future days, nearest on top
for i in range(n - 1, -1, -1):
t = temperatures[i]
while st and temperatures[st[-1]] <= t: # not warmer -> shadowed forever
st.pop()
if st:
ans[i] = st[-1] - i # distance to the next warmer day
st.append(i) # push the INDEX, not the value
return ans7Code line by line
| line | what it means |
|---|---|
| st = [] | Holds indices. Their temperatures go down from bottom to top. |
| for i in range(n - 1, -1, -1): | Walk from the last day to the first. |
| while st and temperatures[st[-1]] <= t: st.pop() | Look up the top day's temperature through its index. If it's not warmer than today, drop it for good. |
| if st: ans[i] = st[-1] - i | The top is the first warmer future day. The answer is the index gap. If the stack is empty, keep 0. |
| st.append(i) | Today is now the nearest future day for yesterday. |
8Dry run: [73, 74, 75, 71, 69, 72, 76, 73]
Stack entries are written as index(temp) to make reading easy. The real stack holds only the index.
| i | current | what we pop (and why) | push | stack AFTER (bottom → top) | answer so far |
|---|---|---|---|---|---|
| 7 | 73 | nothing (empty) → 0 | 7 | [7(73)] | [0,0,0,0,0,0,0,0] |
| 6 | 76 | pop 7(73): 73 ≤ 76 · empty → 0 | 6 | [6(76)] | [0,0,0,0,0,0,0,0] |
| 5 | 72 | nothing: 76 > 72 → 6−5 = 1 | 5 | [6(76), 5(72)] | [0,0,0,0,0,1,0,0] |
| 4 | 69 | nothing: 72 > 69 → 5−4 = 1 | 4 | [6(76), 5(72), 4(69)] | [0,0,0,0,1,1,0,0] |
| 3 | 71 | pop 4(69): 69 ≤ 71 · 72 > 71 → 5−3 = 2 | 3 | [6(76), 5(72), 3(71)] | [0,0,0,2,1,1,0,0] |
| 2 | 75 | pop 3(71), pop 5(72): both ≤ 75 · 76 > 75 → 6−2 = 4 | 2 | [6(76), 2(75)] | [0,0,4,2,1,1,0,0] |
| 1 | 74 | nothing: 75 > 74 → 2−1 = 1 | 1 | [6(76), 2(75), 1(74)] | [0,1,4,2,1,1,0,0] |
| 0 | 73 | nothing: 74 > 73 → 1−0 = 1 | 0 | [6(76), 2(75), 1(74), 0(73)] | [1,1,4,2,1,1,0,0] |
9Complexity & remember
- Time O(n): the for loop runs n times. Across the whole run, the stack sees n pushes and at most n pops. Even in the worst case, where one hot day pops everything, each index is popped only once. Total ≈ 2n = O(n).
- Space O(n) for the stack (e.g. temperatures rising every day keep every index on the stack when walking from the right).
temperatures[st[-1]], answer with st[-1] - i. Pop on <=.Part C · The same stack walking from the left
1The question
The teacher answers a natural doubt: "Must we start from the right? Can't we go from the left?" We can. The code is almost the same, but where we write the answer changes.
2What the constraints tell us
Nothing new: still O(n) time and O(n) space, so both directions pass.
3Intuition: the stack becomes a waiting room
Walking from the left, when we stand on day i we don't know the future yet. So we can't answer day i right now. Instead we flip the roles:
- The stack holds the days that are still waiting for a warmer day. Think of a waiting room.
- Today (day
i) might be the warmer day that some of those waiting days are looking for. If today is warmer than the top waiting day, today is that day's answer. Pop it and writeans[top] = i − top. - Keep popping while today is warmer than the top. Then today joins the waiting room.
| from the right (Part B) | from the left (Part C) | |
|---|---|---|
| the stack holds | candidates: future days that could be someone's answer | questions: past days still waiting for an answer |
| we pop when | top is not warmer than today (<=) | today is warmer than the top (>) |
| a popped day means | shadowed forever, useless | answered and done |
| answer written at | ans[i] = st[-1] - i | ans[top] = i - top |
| days left in the stack at the end | (don't matter) | never got warmer → keep 0 |
4Building the logic from the example
- Day 0 (73): no one is waiting. Push 0.
- Day 1 (74): 74 > 73, so day 0 is answered: ans[0] = 1 − 0 = 1. Pop it, push 1.
- Day 2 (75): answers day 1 → 1. Push 2.
- Day 3 (71) and day 4 (69): colder than the top, so they just join the waiting room. Stack: 2(75), 3(71), 4(69).
- Day 5 (72): warmer than 69 → ans[4] = 1. Warmer than 71 → ans[3] = 5 − 3 = 2. Not warmer than 75 → stop. Push 5.
- Day 6 (76): answers day 5 (1) and day 2 (6 − 2 = 4). Push 6.
- Day 7 (73): colder than 76 → push. Days 6 and 7 stay waiting forever → their answer stays 0.
→ The waiting room is ordered: going down from the top, temperatures never get colder. A day only stays below another if the newer day was not warmer than it. So if today isn't warmer than the top, it isn't warmer than anything below either. We can stop at the first failure.
→ Equal is not warmer, so we do not pop on equal (
>, not >=). Both equal days wait together, and a later warmer day answers both.5Approach steps
ans = [0] * n, empty stack.- For i from 0 to n−1, with
t = temperatures[i]: - While the stack is not empty and
t > temperatures[top]: popj, setans[j] = i − j. - Push i.
- Return ans (whatever is still waiting keeps 0).
6Code (Python)
class Solution:
def dailyTemperatures(self, temperatures):
n = len(temperatures)
ans = [0] * n
st = [] # indices of days still waiting
for i in range(n):
t = temperatures[i]
while st and t > temperatures[st[-1]]: # today is warmer than the top waiter
j = st.pop()
ans[j] = i - j # answer goes to the POPPED day
st.append(i) # today starts waiting
return ans7Code line by line
| line | what it means |
|---|---|
| for i in range(n): | Normal left-to-right walk. |
| while st and t > temperatures[st[-1]]: | Does today end the wait of the top day? Strictly warmer only. |
| j = st.pop() ans[j] = i - j | Day j has found its warmer day (today). Store the gap at j, not at i. |
| st.append(i) | Today hasn't found its warmer day yet, so it waits. |
| return ans | Days left in the stack never got an answer, so their 0 stays. |
8Dry run
| i | current | what we pop (and why) | push | stack AFTER | answer so far |
|---|---|---|---|---|---|
| 0 | 73 | nothing (empty) | 0 | [0(73)] | [0,0,0,0,0,0,0,0] |
| 1 | 74 | pop 0: 74 > 73 → ans[0] = 1 | 1 | [1(74)] | [1,0,0,0,0,0,0,0] |
| 2 | 75 | pop 1: 75 > 74 → ans[1] = 1 | 2 | [2(75)] | [1,1,0,0,0,0,0,0] |
| 3 | 71 | nothing: 71 < 75 | 3 | [2(75), 3(71)] | same |
| 4 | 69 | nothing: 69 < 71 | 4 | [2(75), 3(71), 4(69)] | same |
| 5 | 72 | pop 4 → ans[4] = 1 · pop 3 → ans[3] = 2 · stop at 75 | 5 | [2(75), 5(72)] | [1,1,0,2,1,0,0,0] |
| 6 | 76 | pop 5 → ans[5] = 1 · pop 2 → ans[2] = 4 | 6 | [6(76)] | [1,1,4,2,1,1,0,0] |
| 7 | 73 | nothing: 73 < 76 | 7 | [6(76), 7(73)] | [1,1,4,2,1,1,0,0] |
9Complexity & remember
- Time O(n): each index is pushed once and popped at most once.
- Space O(n): e.g. falling temperatures keep every day waiting.
ans[i] = st[-1] - i). From the left: today answers the popped days (ans[j] = i - j). Same stack, same O(n). Only the answer's address changes.Part D · Revision page
| Brute force | Stack from right | Stack from left | |
|---|---|---|---|
| stack holds | no stack | future days that could still be the answer | past days still waiting |
| pop when | - | temp[top] <= t | t > temp[top] |
| write | ans[i] = j - i | ans[i] = st[-1] - i | ans[j] = i - j for each popped j |
| time / space | O(n²) / O(1) | O(n) / O(n) | O(n) / O(n) |
| Next Greater Element | Daily Temperatures | |
|---|---|---|
| answer | the bigger value | the distance to it |
| stack stores | values | indices (value = temperatures[idx]) |
| not found | −1 | 0 |
2. We need distances → push indices.
3. From the right: pop while
temp[top] <= t, then ans[i] = top − i, push i.4. From the left: while today is warmer than the top, pop j and set
ans[j] = i − j. Push i.5. Each index is pushed once and popped once → O(n).
✗ writing
ans[i] in the left-to-right version (it belongs to the popped day)✗ using
>= for "warmer" (equal is not warmer)✗ comparing
st[-1] (an index) directly with a temperature✗ peeking at an empty stack
s = Solution() print(s.dailyTemperatures([73, 74, 75, 71, 69, 72, 76, 73])) # [1, 1, 4, 2, 1, 1, 0, 0] print(s.dailyTemperatures([30, 40, 50, 60])) # [1, 1, 1, 0] print(s.dailyTemperatures([30, 60, 90])) # [1, 1, 0] print(s.dailyTemperatures([70, 70, 70])) # [0, 0, 0] print(s.dailyTemperatures([50])) # [0]
Based on this video: Daily Temperatures | Monotonic Stack