Skip to content

Commit 37509de

Browse files
committed
Complete Chapter 8 (Stacks & Queues): matching, min stack, monotonic stacks, histogram, RPN, remove k digits
1 parent 8114214 commit 37509de

10 files changed

Lines changed: 1606 additions & 0 deletions

‎CodingInterviewFightClub/src/SUMMARY.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,13 @@
9494
- [7.5 IPO (Maximize Capital)](ch07-heaps/ipo.md)
9595
- [7.6 Meeting Rooms III](ch07-heaps/meeting-rooms-iii.md)
9696
- [7.7 Single Threaded CPU](ch07-heaps/single-threaded-cpu.md)
97+
98+
- [8. Stacks & Queues](ch08-stacks/index.md)
99+
- [8.0 Pattern Primer: LIFO, FIFO, and the Monotonic Stack](ch08-stacks/pattern-primer.md)
100+
- [8.1 Valid Parentheses](ch08-stacks/valid-parentheses.md)
101+
- [8.2 Min Stack](ch08-stacks/min-stack.md)
102+
- [8.3 Daily Temperatures](ch08-stacks/daily-temperatures.md)
103+
- [8.4 Next Greater Element II](ch08-stacks/next-greater-element-ii.md)
104+
- [8.5 Largest Rectangle In Histogram](ch08-stacks/largest-rectangle-in-histogram.md)
105+
- [8.6 Evaluate Reverse Polish Notation](ch08-stacks/evaluate-reverse-polish-notation.md)
106+
- [8.7 Remove K Digits](ch08-stacks/remove-k-digits.md)
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# 8.3 Daily Temperatures
2+
3+
> **Source:** [`src/main/kotlin/stack/DailyTemperatures.kt`](https://github.com/arpanpathak/AdvancedAlgorithmPatterns/blob/main/src/main/kotlin/stack/DailyTemperatures.kt)
4+
> **Pattern:** monotonic stack · **Core page**
5+
6+
## The Problem
7+
8+
Given an array `temperatures` (daily highs), return an array `answer` where `answer[i]` is the number of days you must wait until a **warmer** day — or `0` if none ever comes.
9+
10+
- Constraints: $1 \le n \le 10^5$; $30 \le temperatures[i] \le 100$.
11+
12+
## Examples
13+
14+
```
15+
Input: temperatures = [73,74,75,71,69,72,76,73]
16+
Output: [1,1,4,2,1,1,0,0]
17+
73 -> 74 (1 day), 75 -> 76 (4 days), 71 -> 72 (1 day)... last two never warmer
18+
19+
Input: temperatures = [30,40,50,60]
20+
Output: [1,1,1,0]
21+
```
22+
23+
## Intuition — "who am I waiting for?" is a stack of unresolved days
24+
25+
The naive double loop (for each day, scan forward for the first warmer) is $O(n^2)$. The monotonic-stack insight: **a day only stays unresolved while the following days keep getting colder.** The moment a warmer day arrives, *all* unresolved colder days that came before it get resolved at once — each by this same day.
26+
27+
So maintain a stack of **indices of unresolved days**, in *decreasing temperature order* (the stack top is the coldest among them). For each new day `i`:
28+
29+
- while the stack is non-empty and `temperatures[i] > temperatures[stack.top]` — day `i` is the first warmer day for the top: resolve it (`answer[top] = i - top`), pop;
30+
- then push `i` (unresolved, for now).
31+
32+
The "while" is the resolve-all-the-cold-ones sweep. Each index is pushed once and popped once, so the whole run is $O(n)$ — the amortization argument from the [primer](pattern-primer.md) in action.
33+
34+
**Why indices, not temperatures?** The answer needs *distance* (`i - top`). Storing temperatures would force a parallel array of positions; storing indices gives both the value (`temperatures[top]`) and the position. This is the same "store indices" lesson as [8.5](largest-rectangle-in-histogram.md).
35+
36+
**What stays on the stack?** The unresolved *decreasing* tail — days that have no warmer day to their right yet. When the sweep ends, everything still on the stack gets `0` (the `IntArray` default) — no warmer day ever comes.
37+
38+
## Approach 1 — Nested scan (too slow)
39+
40+
For each day, scan right until a warmer day: worst case $O(n^2)$ (a strictly decreasing array scans $n, n-1, \ldots, 1$).
41+
42+
## Approach 2 — Monotonic stack (the repo's version, optimal)
43+
44+
```kotlin
45+
class DailyTemperatures {
46+
/**
47+
* @param temperatures daily temperatures
48+
* @return days until a warmer day, 0 if none
49+
*/
50+
fun dailyTemperatures(temperatures: IntArray): IntArray {
51+
val result = IntArray(temperatures.size) { 0 }
52+
val stack = mutableListOf<Int>() // indices of unresolved days, decreasing temps
53+
54+
for (i in temperatures.indices) {
55+
// Day i resolves every unresolved colder day above it on the stack
56+
while (stack.isNotEmpty() && temperatures[i] > temperatures[stack.last()]) {
57+
val idx = stack.removeLast()
58+
result[idx] = i - idx // first warmer day is i
59+
}
60+
stack.add(i) // i stays unresolved (for now)
61+
}
62+
return result // stack leftovers already 0
63+
}
64+
}
65+
```
66+
67+
```java
68+
import java.util.*;
69+
70+
public class DailyTemperatures {
71+
/**
72+
* @param temperatures daily temperatures
73+
* @return days until a warmer day, 0 if none
74+
*/
75+
public int[] dailyTemperatures(int[] temperatures) {
76+
int[] result = new int[temperatures.length];
77+
Deque<Integer> stack = new ArrayDeque<>(); // indices of unresolved days
78+
79+
for (int i = 0; i < temperatures.length; i++) {
80+
while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
81+
int idx = stack.pop();
82+
result[idx] = i - idx; // first warmer day is i
83+
}
84+
stack.push(i);
85+
}
86+
return result; // leftovers already 0
87+
}
88+
}
89+
```
90+
91+
```cpp
92+
#include <stack>
93+
#include <vector>
94+
95+
class DailyTemperatures {
96+
public:
97+
/**
98+
* @param temperatures daily temperatures
99+
* @return days until a warmer day, 0 if none
100+
*/
101+
std::vector<int> dailyTemperatures(std::vector<int>& temperatures) {
102+
std::vector<int> result(temperatures.size(), 0);
103+
std::stack<int> st; // indices of unresolved days
104+
105+
for (int i = 0; i < (int)temperatures.size(); i++) {
106+
while (!st.empty() && temperatures[i] > temperatures[st.top()]) {
107+
int idx = st.top(); st.pop();
108+
result[idx] = i - idx; // first warmer day is i
109+
}
110+
st.push(i);
111+
}
112+
return result; // leftovers already 0
113+
}
114+
};
115+
```
116+
117+
```python
118+
def daily_temperatures(temperatures: list[int]) -> list[int]:
119+
"""
120+
@param temperatures: daily temperatures
121+
@return: days until a warmer day, 0 if none
122+
"""
123+
result = [0] * len(temperatures)
124+
stack = [] # indices of unresolved days
125+
126+
for i, temp in enumerate(temperatures):
127+
while stack and temp > temperatures[stack[-1]]:
128+
idx = stack.pop()
129+
result[idx] = i - idx # first warmer day is i
130+
stack.append(i)
131+
return result
132+
```
133+
134+
```rust
135+
impl Solution {
136+
/// @param temperatures daily temperatures
137+
/// @return days until a warmer day, 0 if none
138+
pub fn daily_temperatures(temperatures: Vec<i32>) -> Vec<i32> {
139+
let mut result = vec![0; temperatures.len()];
140+
let mut stack: Vec<usize> = Vec::new(); // indices of unresolved days
141+
142+
for i in 0..temperatures.len() {
143+
while let Some(&idx) = stack.last() {
144+
if temperatures[i] <= temperatures[idx] { break; }
145+
result[idx] = (i - idx) as i32; // first warmer day is i
146+
stack.pop();
147+
}
148+
stack.push(i);
149+
}
150+
result
151+
}
152+
}
153+
```
154+
155+
## Dry run
156+
157+
**Input:** `temperatures = [73,74,75,71,69,72,76,73]`.
158+
159+
```
160+
i=0 (73): stack empty -> push 0. stack=[0]
161+
i=1 (74): 74 > 73 -> resolve 0: result[0]=1, pop. stack=[]
162+
push 1. stack=[1]
163+
i=2 (75): 75 > 74 -> result[1]=1, pop. stack=[]
164+
push 2. stack=[2]
165+
i=3 (71): 71 > 75? no -> push 3. stack=[2,3]
166+
i=4 (69): 69 > 71? no -> push 4. stack=[2,3,4]
167+
i=5 (72): 72 > 69 -> result[4]=1, pop.
168+
72 > 71 -> result[3]=2, pop. stack=[2]
169+
72 > 75? no. push 5. stack=[2,5]
170+
i=6 (76): 76 > 72 -> result[5]=1, pop.
171+
76 > 75 -> result[2]=4, pop. stack=[]
172+
push 6. stack=[6]
173+
i=7 (73): 73 > 76? no -> push 7. stack=[6,7]
174+
end: stack leftovers {6,7} keep result 0.
175+
176+
result = [1,1,4,2,1,1,0,0] ✓
177+
```
178+
179+
The pivotal moment is i=5: one day (`72`) resolves *two* unresolved colder days at once (69 at distance 1, 71 at distance 2). That's the monotonic-stack efficiency — each element resolved exactly once, by exactly one later day.
180+
181+
## Complexity
182+
183+
**Time.** Each index pushed once, popped once:
184+
185+
$$
186+
T(n) = O(n)
187+
$$
188+
189+
**Space.** The stack of unresolved indices:
190+
191+
$$
192+
S(n) = O(n)
193+
$$
194+
195+
## Variants & follow-ups
196+
197+
- **Next Greater Element I / II** ([8.4](next-greater-element-ii.md)) — same engine, values instead of distances; the circular version doubles the array conceptually.
198+
- **Online Stock Span** (`src/main/kotlin/stack/OnlineStockSpan.kt`) — the "next greater to the left" mirror: the span is the distance to the *previous* larger element.
199+
- **Sum Of Subarray Minimums** (`src/main/kotlin/stack/SumOfSubArrayMinimum.kt`) — the deepest descendant: every subarray's minimum is found by this same monotonic stack, then summed by a counting argument.
200+
- **Interview follow-up:** "Why does the stack stay decreasing?" A day only stays unresolved if every later day so far is colder — so unresolved indices form a decreasing temperature sequence. The `while` pop preserves that invariant, which is what makes "the first warmer day" computable in one sweep.

0 commit comments

Comments
 (0)