1. 题目解析与核心思路
LeetCode 496题"Next Greater Element I"是一道经典的栈应用题目,属于单调栈的入门级练习题。题目给定两个没有重复元素的数组nums1和nums2,其中nums1是nums2的子集。要求找出nums1中每个元素在nums2中的位置,并找到该位置右侧第一个比它大的元素,如果不存在则输出-1。
这道题的关键在于理解"下一个更大元素"的定义。举个例子:
- nums1 = [4,1,2]
- nums2 = [1,3,4,2]
对于数字4,在nums2中右侧没有比它大的元素,所以输出-1
对于数字1,在nums2中右侧第一个比它大的是3
对于数字2,在nums2中右侧没有元素,所以输出-1
最终结果应该是[-1,3,-1]
1.1 暴力解法分析
最直观的解法是双重循环:
- 遍历nums1中的每个元素
- 在nums2中找到该元素的位置
- 从该位置开始向右扫描,找到第一个比它大的元素
这种方法的时间复杂度是O(n*m),其中n是nums1的长度,m是nums2的长度。虽然在小数据量时可以工作,但显然不是最优解。
注意:在面试中如果只给出暴力解法,通常会被要求优化。面试官期待的是更高效的单调栈解法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 单调栈解法详解
单调栈是解决这类"下一个更大元素"问题的标准解法。它的核心思想是维护一个栈,保证栈内元素从栈底到栈顶是单调递减的。这样当遇到一个新元素时,可以快速确定哪些元素的"下一个更大元素"是这个新元素。
2.1 算法步骤
- 初始化一个空栈和一个空哈希表(用于存储结果)
- 遍历nums2中的每个元素:
- 当栈不为空且当前元素大于栈顶元素时:
- 弹出栈顶元素
- 在哈希表中记录该栈顶元素的下一个更大元素是当前元素
- 将当前元素压入栈中
- 当栈不为空且当前元素大于栈顶元素时:
- 遍历nums1,从哈希表中查找每个元素对应的结果,如果没有则记为-1
2.2 Python实现代码
python复制def nextGreaterElement(nums1, nums2):
stack = []
mapping = {}
for num in nums2:
while stack and num > stack[-1]:
mapping[stack.pop()] = num
stack.append(num)
return [mapping.get(num, -1) for num in nums1]
2.3 复杂度分析
- 时间复杂度:O(n + m),其中n是nums1的长度,m是nums2的长度。每个元素最多入栈和出栈一次。
- 空间复杂度:O(m),用于存储栈和哈希表。
3. 关键点与常见错误
3.1 为什么使用单调栈?
单调栈能够高效解决这类"下一个更大/更小元素"问题,因为它:
- 将O(n^2)的时间复杂度降为O(n)
- 通过一次遍历就能确定所有元素的下一个更大元素
- 栈结构天然适合处理这种"最近邻"关系的问题
3.2 常见实现错误
- 栈的顺序错误:应该保持栈顶到栈底是递增还是递减?对于"下一个更大元素"问题,需要保持栈内元素单调递减。
- 哈希表更新时机:只有在弹出元素时才需要更新哈希表,表示找到了它的下一个更大元素。
- 剩余栈处理:遍历结束后栈中剩余的元素没有下一个更大元素,应该对应-1,这部分在代码中通过mapping.get(num, -1)自动处理了。
提示:可以在纸上手动模拟算法过程,比如对于nums2=[1,3,4,2],一步步画出栈和哈希表的变化,这样能加深理解。
4. 变种与扩展
4.1 循环数组情况
LeetCode 503题是这道题的变种,要求处理循环数组。解决方案是:
- 将数组长度翻倍(模拟循环)
- 使用相同的单调栈方法
- 只取前n个结果
4.2 前一个更小元素
类似地,可以解决"前一个更小元素"问题,只需要:
- 改变单调栈的单调方向(递增栈)
- 从右向左遍历数组
5. 实际应用场景
这类问题在实际中有多种应用:
- 股票分析:找出某天后第一个更高的股价
- 温度预测:找出某天后第一个更高的温度(LeetCode 739题)
- 数据库查询优化:某些范围查询可以使用类似思想优化
6. 刷题建议
-
同类题目推荐:
-
- Next Greater Element II(循环数组)
-
- Daily Temperatures(温度问题)
-
- Largest Rectangle in Histogram(更难一些的单调栈应用)
-
-
调试技巧:
- 对于单调栈问题,建议先用小例子手动模拟
- 打印出每一步栈的状态和结果映射
- 特别注意边界条件(空栈、数组末尾等)
-
面试准备:
- 能够解释清楚为什么单调栈能解决问题
- 能够分析时间/空间复杂度
- 知道如何处理循环数组等变种
我在刷这道题时发现,真正理解单调栈的工作原理比记住代码更重要。建议先理解暴力解法,再思考如何优化,最后再学习单调栈解法,这样的学习路径更扎实。对于栈内元素单调性的保持,可以想象成"矮个子被高个子挡住"的场景,这样更容易记忆。
