1. 先搞清楚“下一个更大元素”到底在考什么
先直说,这三道题在LeetCode上属于“单调栈”这一类的入门和进阶题。很多刷题的朋友一看到“每日温度”“下一个更大元素”这种名字,觉得是两套东西,实际上它们在考同一个核心模型:给定一个序列,对每个位置,找右边第一个比它大的元素。区别只在于题目的包装和边界条件。
我当年刷到这三题的时候,正好是准备面试的阶段,一开始也是硬用暴力解,两层循环去扫描,代码写出来当然能过样例,但一到LeetCode的测试用例就超时。后来才意识到,这类题想让你练的根本不是“能不能找到答案”,而是如何把时间复杂度从O(n²)压到O(n)。面试官在考算法题的时候,最看重的往往就是这个优化过程——你能不能识别出“重复计算在哪里”,然后用什么数据结构把它消掉。
先给没接触过的朋友扫个盲。所谓“下一个更大元素”,意思是:在一个数组里,对某个位置i,找到右边离它最近的那个位置j,使得arr[j] > arr[i],这个arr[j]就是“下一个更大元素”。如果右边没有比它大的,就返回-1(或者0,看题目怎么要求)。比如数组[2, 1, 4, 3],下标0的值是2,右边第一个比2大的是下标2的值4,所以答案是4。这种问题如果两层循环暴力解,每个元素都要往右扫描,最坏情况下就是O(n²),数据量一大就完蛋。
那单调栈为什么能优化?思路其实很朴素:你在从左往右遍历的时候,如果当前元素比栈顶元素大,说明当前元素就是栈顶那个元素的“下一个更大元素”,这时候就可以把栈顶元素弹出,记录答案,然后继续比较新的栈顶。这个过程中,每个元素最多入栈一次、出栈一次,所以总的时间复杂度是O(n)。这个思想理解透了,三道题基本就是套模板的事情。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 739. 每日温度:单调栈最经典的入门场景
2.1 题目到底让你算什么
题目是给一个数组temperatures,表示未来每天的气温,要求返回一个等长的数组answer,其中answer[i]表示:对于第i天,需要等多少天才能等到一个比当天更高的温度。如果后面都没有更高的,answer[i]就等于0。
举个例子,temperatures = [73, 74, 75, 71, 69, 72, 76, 73],输出应该是[1, 1, 4, 2, 1, 1, 0, 0]。
这个题和“下一个更大元素”的本质关系非常直接:我们要找的不是“下一个更大元素的值”,而是“下一个更大元素离当前多远”。换句话说,单调栈里存的不能只是值,还要能算出来“距离”。这里有个关键点:单调栈里存的是下标,而不是值。为什么要存下标?因为只有拿到下标,才能算出两个位置之间的距离。这是很多新手第一次写这道题最容易卡住的地方——想存值,结果发现求不出天数。
从另一个角度看,这道题非常适合作为单调栈的“第一课”,因为它的场景非常生活化。气温的爬升和下降是随机的,我们想知道“下一次升温还要等多久”,本质上就是在问“右边第一个比当前大的位置在哪里”。一旦你理解了气温这个场景,后面再看到“下一个更大元素”这类抽象题,就很容易联想到同一个模型。
2.2 入栈出栈的具体逻辑与代码实现
先说栈的维护原则:我们从左往右遍历,维护一个栈底到栈顶递减的栈(也就是栈顶元素最小)。为什么是递减?因为我们希望在遍历的过程中,一旦遇到一个更大的元素,就把它和栈里所有比它小的元素配对。配对的顺序是从栈顶开始,所以栈顶必须是最“近”的那个还没配对的元素。
具体流程是这样的:
- 遍历temperatures的每个下标i。
- 如果栈不为空,并且当前温度temperatures[i]大于栈顶下标对应的温度temperatures[stack.peek()],就说明当前这个i就是栈顶那个下标的“下一个更高温度”,把栈顶弹出,记录距离为i - popIndex。
- 重复第2步,直到栈为空或者当前温度不大于栈顶温度。
- 把当前下标i入栈。
原因其实很清晰:栈里存的是“还在等待匹配”的下标。只要当前元素比栈顶元素大,栈顶元素就“等到”了它的结果,可以出栈了。如果当前元素不比栈顶大,那当前元素可能比栈里更下面的元素大吗?不可能,因为栈是往栈底递增的,所以当前元素也没法匹配更下面的元素,只能自己先入栈,等着后面更大的元素来解救它。
代码我直接贴出来,用Java写,因为面试里用Java的比较多:
java复制class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] answer = new int[n];
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && temperatures[
