单调栈解决每日温度问题
本文旨在通过详细解析算法题739 - “每日温度”,演示如何利用单调栈结构高效解决实际问题。题目要求根据每天的温度,计算出距离下一次温度升高所需的天数。本文将提供Python、C++和Java三种语言实现的代码,并对解题思路和单调栈的应用进行深入分析。
题目概述
给定一个整数数组temperatures
,表示每天的温度。要求返回一个数组answer
,其中answer[i]
是指对于第i
天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用0
来代替。
示例
- 输入:
temperatures = [73, 74, 75, 71, 69, 72, 76, 73]
输出:[1, 1, 4, 2, 1, 1, 0, 0]
- 输入:
temperatures = [30, 40, 50, 60]
输出:[1, 1, 1, 0]
- 输入:
temperatures = [30, 60, 90]
输出:[1, 1, 0]
解题思路
核心思想:单调栈
题目分析提示我们使用单调栈来解决这个问题。单调栈是一种特殊的栈结构,其在栈内部保持元素的单调性(递增或递减)。在本题中,我们使用递减的单调栈来存储温度的索引。
对于每个元素,我们需要找到它右侧第一个比它大的元素的索引。如果当前元素大于栈顶元素对应的温度,则栈顶元素出栈,此时我们找到了栈顶元素的“下一个更高温度”。
单调栈算法详解
单调栈算法是一种特殊的栈结构应用,它在处理一系列数据(如数组元素)时,通过维护栈内元素的单调性(递增或递减)来高效解决一类特定的问题。这种算法在处理序列问题,如找出数组中元素的下一个更大(或更小)元素时表现出色。
核心概念
-
单调性: 栈内元素保持严格的单调递增或递减顺序。通常,单调递增栈用于找出元素的“下一个更小值”,而单调递减栈用于找出元素的“下一个更大值”。
-
栈操作: 在处理每个元素时,会进行入栈和出栈操作。当遇到破坏单调性的元素时,会进行连续的出栈操作,直到恢复栈的单调性为止,然后再将新元素入栈。
应用场景
单调栈适用于各种需要查询元素之间特定关系的问题,如:
- 在一个数组中,寻找每个元素右侧第一个比它大(或小)的元素。
- 计算数组中每个元素向左延伸和向右延伸的距离,如在直方图中找到每个条形图能扩展的最大宽度。
- 某些字符串匹配和处理问题,其中元素的顺序和它们之间的相对位置很重要。
算法特点
- 高效性: 单调栈算法通常能够以线性时间复杂度处理问题,因为每个元素最多被压入和弹出栈一次。
- 直观: 算法实现相对简洁直观,便于理解和应用。
- 适用范围有限: 单调栈不是通用解决方案,它只适用于能够通过维护序列中元素的单调关系来解决的问题。
实现策略
单调栈的实现通常遵循以下步骤:
- 初始化空栈: 创建一个空栈来存储序列中的元素索引。
- 遍历元素: 从头开始或从尾开始遍历序列中的元素。
- 维护栈的单调性:
- 如果当前元素符合栈的单调性,则将其索引压入栈中。
- 如果当前元素破坏了栈的单调性,则连续弹出栈顶元素,直到栈变为单调。在每次弹出时,根据问题需求处理弹出的元素。
- 记录结果: 在处理每个元素时,根据问题需求更新结果。
算法流程
- 初始化一个空栈和答案数组,答案数组中的所有元素初始为
0
。 - 从后向前遍历温度数组。
- 对于每个元素,不断弹出栈顶,直到栈顶元素对应的温度大于当前温度。
- 如果栈不为空,栈顶元素与当前元素的差值即为答案数组中当前位置的值。
- 将当前元素的索引压入栈中。
- 返回答案数组。
代码实现
Python 实现
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
stk = []
n = len(temperatures)
ans = [0] * n
for i in range(n - 1, -1, -1):
while stk and temperatures[stk[-1]] <= temperatures[i]:
stk.pop()
if stk:
j = stk[-1]
ans[i] = j - i
stk.append(i)
return ans
C++ 实现
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
stack<int> stk;
vector<int> ans(n, 0);
for (int i = n - 1; i >= 0; --i) {
while (!stk.empty() && temperatures[stk.top()] <= temperatures[i]) {
stk.pop();
}
if (!stk.empty()) {
int j = stk.top();
ans[i] = j - i;
}
stk.push(i);
}
return ans;
}
};
Java 实现
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
Stack<Integer> stk = new Stack<>();
int n = temperatures.length;
int[] ans = new int[n];
for (int j = n - 1
; j >= 0; --j) {
while (!stk.empty() && temperatures[stk.peek()] <= temperatures[j]) {
stk.pop();
}
if (!stk.empty()) {
int i = stk.peek();
ans[j] = i - j;
}
stk.push(j);
}
return ans;
}
}
结语
通过上述解析,我们可以看到单调栈在解决特定类型问题时的高效性和实用性。希望本文的解析对您有所帮助。如有疑问或建议,欢迎留言交流。