Files
codetutor/backend/data/questions/daily-temperatures.yaml
2025-05-25 11:08:40 +01:00

168 lines
8.3 KiB
YAML

title: Daily Temperatures
slug: daily-temperatures
difficulty: medium
leetcode_id: 739
leetcode_url: https://leetcode.com/problems/daily-temperatures/
categories:
- arrays
- stack
patterns:
- monotonic-stack
description: |
Given an array of integers `temperatures` represents the daily temperatures, return *an array* `answer` *such that* `answer[i]` *is the number of days you have to wait after the* i<sup>th</sup> *day to get a warmer temperature*.
If there is no future day for which this is possible, keep `answer[i] == 0` instead.
constraints: |
- `1 <= temperatures.length <= 10^5`
- `30 <= temperatures[i] <= 100`
examples:
- input: "temperatures = [73,74,75,71,69,72,76,73]"
output: "[1,1,4,2,1,1,0,0]"
explanation: "Day 0: 73, warmer on day 1 (74), wait 1 day. Day 1: 74, warmer on day 2 (75), wait 1 day. Day 2: 75, warmer on day 6 (76), wait 4 days. Day 3: 71, warmer on day 5 (72), wait 2 days. Day 4: 69, warmer on day 5 (72), wait 1 day. Day 5: 72, warmer on day 6 (76), wait 1 day. Days 6-7 have no warmer future day, so answer is 0."
- input: "temperatures = [30,40,50,60]"
output: "[1,1,1,0]"
explanation: "Each day is immediately followed by a warmer day, except the last day which has no future days."
- input: "temperatures = [30,60,90]"
output: "[1,1,0]"
explanation: "Strictly increasing temperatures, so each day (except the last) waits just 1 day for a warmer temperature."
explanation:
intuition: |
Imagine you're tracking weather and for each day you want to know: "How many days until it gets warmer?"
The naive approach would be to look ahead from each day until you find a warmer one, but this could be very slow. Instead, think about it differently: **as you encounter each day, which previous days are "waiting" for this temperature?**
Picture a stack of days that are still waiting for a warmer temperature. When you see a new day's temperature, you check if it's warmer than any of the waiting days. If it is, those days have found their answer! They can be "resolved" and removed from the waiting stack.
The key insight is that days with higher temperatures will stay in the stack longer, while days with lower temperatures get resolved quickly. This creates a **monotonically decreasing stack** - temperatures in the stack always decrease from bottom to top. When a new temperature breaks this pattern (it's higher than the top), we pop and resolve all the days that are now "answered".
approach: |
We solve this using a **Monotonic Stack** that tracks indices of days waiting for a warmer temperature:
**Step 1: Initialise data structures**
- `answer`: Array of size `n` filled with `0` (default: no warmer day found)
- `stack`: Empty stack to hold indices of days awaiting warmer temperatures
&nbsp;
**Step 2: Iterate through each day**
- For each index `i` with temperature `temperatures[i]`:
- While the stack is not empty AND current temperature is warmer than the temperature at the index on top of the stack:
- Pop the index from the stack (let's call it `prev_idx`)
- Calculate the wait time: `i - prev_idx`
- Store this in `answer[prev_idx]`
- Push the current index `i` onto the stack (this day now waits for its warmer day)
&nbsp;
**Step 3: Return the result**
- Return `answer` - any indices still in the stack never found a warmer day, but they're already `0` by default
&nbsp;
This approach works because we process days in order. When we find a warmer day, all colder days waiting on the stack get their answer immediately. Days that never find a warmer temperature remain at `0`.
common_pitfalls:
- title: The Brute Force Trap
description: |
The instinctive approach is to use nested loops: for each day `i`, scan forward to find the first warmer day.
```python
for i in range(n):
for j in range(i + 1, n):
if temperatures[j] > temperatures[i]:
answer[i] = j - i
break
```
This is **O(n^2)** time complexity. With `temperatures.length <= 10^5`, this means up to 10 billion operations in the worst case (strictly decreasing temperatures), causing **Time Limit Exceeded (TLE)**.
wrong_approach: "Nested loops scanning forward from each day"
correct_approach: "Monotonic stack resolving days in reverse order"
- title: Storing Temperatures Instead of Indices
description: |
A common mistake is pushing temperatures onto the stack instead of indices.
You need indices for two reasons:
1. To calculate the wait time (`i - prev_idx`)
2. To update the correct position in the `answer` array
Always push indices and look up temperatures using `temperatures[stack[-1]]`.
wrong_approach: "Pushing temperature values onto the stack"
correct_approach: "Pushing indices and accessing temperatures via index lookup"
- title: Off-by-One in Wait Calculation
description: |
The wait time is `current_index - previous_index`, not `current_index - previous_index - 1`.
If you buy on day 3 and find warmth on day 5, you wait `5 - 3 = 2` days (days 4 and 5). The calculation is a simple subtraction of indices.
wrong_approach: "Using i - prev_idx - 1"
correct_approach: "Using i - prev_idx"
key_takeaways:
- "**Monotonic stack pattern**: When you need to find the next greater/smaller element for each position, a monotonic stack gives O(n) time"
- "**Process in order, resolve backwards**: The stack holds unresolved items; new elements resolve older ones that they satisfy"
- "**Store indices, not values**: In problems requiring position information, push indices and look up values when needed"
- "**Foundation for similar problems**: This pattern applies to Next Greater Element, Stock Span, Largest Rectangle in Histogram, and many more"
time_complexity: "O(n). Each element is pushed onto the stack once and popped at most once, giving 2n operations total."
space_complexity: "O(n). The stack can hold up to n indices in the worst case (strictly decreasing temperatures), plus the output array of size n."
solutions:
- approach_name: Monotonic Stack
is_optimal: true
code: |
def daily_temperatures(temperatures: list[int]) -> list[int]:
n = len(temperatures)
# Initialise answer array with zeros (default: no warmer day)
answer = [0] * n
# Stack stores indices of days waiting for a warmer temperature
stack = []
for i in range(n):
# While current temp is warmer than temp at index on top of stack
while stack and temperatures[i] > temperatures[stack[-1]]:
# Pop the index - this day found its warmer day
prev_idx = stack.pop()
# Calculate how many days it had to wait
answer[prev_idx] = i - prev_idx
# Current day now waits for its warmer day
stack.append(i)
return answer
explanation: |
**Time Complexity:** O(n) — Each index is pushed and popped at most once.
**Space Complexity:** O(n) — Stack can hold up to n indices in the worst case.
The monotonic stack maintains indices in decreasing temperature order. When we encounter a warmer temperature, we pop all indices with lower temperatures and calculate their wait times. This processes all n elements with at most 2n stack operations total.
- approach_name: Brute Force
is_optimal: false
code: |
def daily_temperatures(temperatures: list[int]) -> list[int]:
n = len(temperatures)
answer = [0] * n
# For each day, scan forward to find the next warmer day
for i in range(n):
for j in range(i + 1, n):
if temperatures[j] > temperatures[i]:
answer[i] = j - i
break # Found the first warmer day
return answer
explanation: |
**Time Complexity:** O(n^2) — Nested loops in the worst case (strictly decreasing).
**Space Complexity:** O(1) — Only the output array, no additional space.
This straightforward approach checks every future day for each position. While correct, it's too slow for large inputs and will cause TLE on LeetCode. Included to illustrate why the monotonic stack optimisation is necessary.