193 lines
9.2 KiB
YAML
193 lines
9.2 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:
|
|
- slug: monotonic-stack
|
|
is_optimal: true
|
|
|
|
function_signature: "def daily_temperatures(temperatures: list[int]) -> list[int]:"
|
|
|
|
test_cases:
|
|
visible:
|
|
- input: { temperatures: [73, 74, 75, 71, 69, 72, 76, 73] }
|
|
expected: [1, 1, 4, 2, 1, 1, 0, 0]
|
|
- input: { temperatures: [30, 40, 50, 60] }
|
|
expected: [1, 1, 1, 0]
|
|
- input: { temperatures: [30, 60, 90] }
|
|
expected: [1, 1, 0]
|
|
hidden:
|
|
- input: { temperatures: [50] }
|
|
expected: [0]
|
|
- input: { temperatures: [90, 80, 70, 60] }
|
|
expected: [0, 0, 0, 0]
|
|
- input: { temperatures: [60, 70, 80, 90] }
|
|
expected: [1, 1, 1, 0]
|
|
- input: { temperatures: [55, 55, 55, 55] }
|
|
expected: [0, 0, 0, 0]
|
|
- input: { temperatures: [40, 35, 32, 37, 50] }
|
|
expected: [4, 2, 1, 1, 0]
|
|
- input: { temperatures: [89, 62, 70, 58, 47, 47, 46, 76, 100, 70] }
|
|
expected: [8, 1, 5, 4, 3, 2, 1, 1, 0, 0]
|
|
|
|
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
|
|
|
|
|
|
|
|
**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)
|
|
|
|
|
|
|
|
**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
|
|
|
|
|
|
|
|
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.
|