开头
刷算法题刷到Day48的朋友,肯定对“单调栈”这个词不陌生了。今天要聊的这三道题——739.每日温度、496.下一个更大元素I、503.下一个更大元素II——可以说是单调栈这个套路里最经典的三板斧。它们仨放在一起刷,本质上就是同一套思想在不同场景下的三次应用:暴力解法谁都会写,但数据规模一大就崩;而单调栈能在O(n)时间内解决“找右边第一个比我大/小的元素”这类问题,这才是核心价值。
如果你正准备面试,或者刷题到了单调栈这个专题节点,这三题是你绕不开的基石。把它们彻底吃透,不只是背下代码模板,而是理解“为什么用单调栈”“什么时候该从右往左遍历”“循环数组怎么处理”这些底层逻辑,以后再遇到同类型的题基本就是秒杀。
这篇博文我会把三题放在一起拆,从题目本质、算法选型、代码实现到常见坑位,一条线捋清楚,争取让你看完之后不用再翻别的资料,直接能上手写出来。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
1. 三道题的本质:同一个套路,三种变体
1.1 题目到底在问什么
先说结论:这三道题说的都是同一件事——找“右边第一个比当前元素更大的元素”。
739.每日温度,给你一个温度数组,问每个位置要等几天才能等到一个更高的温度。举个例子,[73, 74, 75, 71, 69, 72, 76, 73],第0天温度73,第1天就是74,所以答案是1;第1天温度74,第2天是75,答案也是1;第2天温度75,后面找了一圈到第6天才是76,所以答案是4。注意这里要求的是“间隔天数”,也就是索引差,而不是更大的值本身。
496.下一个更大元素I,题目绕了一层。给你两个数组,nums2是完整数组,nums1是nums2的子集。对nums1里的每个元素,去nums2里找它右边第一个更大的值,找不到就返回-1。比如nums1 = [4, 1, 2],nums2 = [1, 3, 4, 2],4在nums2右边没有更大的,返回-1;1的右边第一个更大值是3,返回3;2的右边没有更大的(它自己是最后一个),返回-1。
503.下一个更大元素II,场景变成了循环数组。也就是说,数组末尾的元素“绕一圈”还能继续往后找。比如[1, 2, 1],第2个元素1,右边找不到更大的,但循环回去以后第0个元素还是1,第1个元素是2,所以答案是2;而第1个元素2,循环一圈也找不到比2更大的,返回-1。
你会发现,这三题的解题框架完全一样,区别只在于:739是求“索引差”,496是“部分元素查询 + 哈希表映射”,503是“数组循环处理 + 取模索引”。
1.2 为什么第739题不能无脑暴力
很多初学者看到739的第一反应是双重循环,外层遍历每一天,内层从i+1开始往后找第一个更大的温度,找到了就记录差值,找不到就写0。这个逻辑没错,时间复杂度是O(n²),提交上去大概率超时。力扣这道题的n范围是10^5级别,O(n²)意味着最坏情况下要跑10^10次操作,在现代计算机上也需要几十秒,显然不行。
那为什么单调栈能把复杂度压到O(n)?核心在于“每个元素最多进栈一次、出栈一次”。你在遍历过程中,每个元素被压入栈一次,弹出一次,整体操作次数就是2n级别。这比暴力解法中每个元素都要往后扫描一大段区间的做法高效得多。
所以这道题的正确思路是:用空间换时间,借助一个单调递减栈(从栈底到栈顶递减)来维护“还没找到右边更大元素”的候选位置。
注意:这里说“单调递减栈”是从栈底到栈顶递减。配合遍历方向不同,有时候我们用的是递增栈,需要先明确自己的定义,不然代码很容易写反。
1.3 单调栈的核心原理:先记住一个生活化模型
单调栈的原理听起来抽象,其实完全可以类比成“排队看身高”。
想象有一排人站成一列,每个人想知道自己右边第一个比自己高的人在哪里。你可以从右往左看,也可以从左往右维护一个“还没被更高的身高淘汰”的候选队列。用从左往右的视角:遍历到一个新元素时,如果它比栈顶元素高,那么栈顶元素右边的第一个更高者就是这个新元素,于是栈顶出栈并记录答案;然后继续比较新的栈顶,直到栈顶比当前元素高或者栈空,最后把当前元素压栈。
用更直白的话说:栈里存的都是“暂时还没找到答案的可怜人”,而且这些人的身高从栈底到栈顶是递减的。新人一来,如果自己更高,就能把栈里那些比自己矮的人全部“解救”,因为自己就是它们右边第一个更高的围墙。而那些比新人高的,留在栈里继续等。
这个模型能帮你快速判断:什么时候出栈,出栈时记录什么,栈里存的是索引还是值。搞清楚了,单调栈的代码就可以像模板一样默写。
2. 739.每日温度:最经典的单调栈入门
2.1 题目的隐含要求与数据规模
先看题目细节:温度数组长度范围是[1, 10^5],温度值在[30, 100]之间。每道题返回的是一个等长数组,answer[i]表示从第i天开始,要等多少天才能等到更高的温度,没有就填0。
注意三个容易忽略的点:
第一,要求的是“间隔天数”而不是“温度差”,所以记录索引差而不是温度差值。
第二,如果右边没有更高温度,结果是0。这意味着结果数组初始
