【算法】【单调栈、JAVA】力扣496. 下一个更大元素 I
题目描述
nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。
给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。
对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。
返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。
问题梳理
- 核心问题:对于
nums1中的每个数字x,找到在数组nums2中,x右侧第一个比x大的元素。 - 输入:两个没有重复元素的数组
nums1和nums2,其中nums1是nums2的子集。 - 输出:一个长度等于
nums1.length的数组ans,表示对于nums1中每个元素,在nums2中的下一个更大元素。
示例
-
示例 1:
- 输入:
nums1 = [4,1,2],nums2 = [1,3,4,2] - 输出:
[-1,3,-1] - 解释:对于
nums1中的每个元素,其在nums2中的下一个更大元素如下:4:在nums2中没有找到更大元素,因此为-1。1:在nums2中下一个更大元素是3。2:在nums2中没有找到更大元素,因此为-1。
- 输入:
-
示例 2:
- 输入:
nums1 = [2,4],nums2 = [1,2,3,4] - 输出:
[3,-1] - 解释:对于
nums1中的每个元素,其在nums2中的下一个更大元素如下:2:在nums2中下一个更大元素是3。4:在nums2中没有找到更大元素,因此为-1。
- 输入:
提示
- 数组
nums1和nums2的长度范围:1 <= nums1.length <= nums2.length <= 1000 - 数组元素的值范围:
0 <= nums1[i], nums2[i] <= 104 nums1和nums2中的所有整数互不相同,且nums1中的所有整数都出现在nums2中。
进阶
- 是否能够设计一个时间复杂度为
O(nums1.length + nums2.length)的解决方案?
引言
进阶要求可以使用单调栈实现,探讨如何使用单调栈解决此题就是我们这篇博客探讨的主要内容。接下来,我将详细介绍解题思路和实现的JAVA代码。
解题思路
首先,我们定义一个单调递减栈来存储那些还没有找到下一个更大元素的数字。我们从数组 nums2 的末尾开始逆序遍历。对于每一个数字,我们检查栈顶元素。如果栈顶元素小于或等于当前数字,那么它不可能是当前数字的下一个更大值。因此,我们将这些元素从栈中移除,直到栈顶元素大于当前数字或者栈为空。
如果栈非空,栈顶元素即为当前数字的下一个更大值。我们使用哈希表来存储这些对应关系,以便于后续快速查询。最后,我们遍历 nums1,根据之前构建的哈希表来填充结果数组。
解题思路详解
在力扣496. 下一个更大元素 I的问题中,关键是高效地找到每个元素的下一个更大值。以下是详细的步骤分解:
步骤一:定义单调递减栈
- 目的:创建一个单调递减栈用来存储可能成为“下一个更大元素”的数字。
- 实现:使用一个双端队列模拟栈(因为这样性能更高)。
Deque<Integer> monotone_stk = new ArrayDeque<>();
步骤二:逆序遍历 nums2
- 目的:栈中存储的都是可能成为下一个更大数字”的元素,对于访问过的数字,它们都位于未访问元素的右侧。逆序遍历
nums2是为了方便地找到每个数字的右侧的下一个更大元素。 - 实现:使用
for循环,从nums2数组的最后一个元素开始,向前遍历每个元素。
步骤三:移除不符合条件的栈顶元素
- 目的:移除所有小于或等于当前数字的栈顶元素,因为它们不可能是当前数字的下一个更大值。
- 实现:当栈不为空且栈顶元素小于或等于当前元素时,持续弹出栈顶元素。
步骤四:记录下一个更大元素
- 目的:在处理掉所有小于等于当前元素的栈顶元素后,当前栈顶元素即为当前数字的下一个更大元素,将其记录起来。
- 实现:使用哈希表来存储当前数字与其下一个更大元素之间的对应关系。
步骤五:将当前数字压入栈中
- 目的:当前数字也是潜在的“下一个更大数字”,需要压入栈中。
- 实现:在移除不符合条件的栈顶元素后,将当前元素压入栈中,保持栈的单调递减性质。
步骤六:遍历 nums1 并构建答案数组
- 目的:构建答案数组。
- 实现:遍历
nums1中的每个元素,使用哈希表找到其对应的下一个更大元素,填充到结果数组中。
通过上述步骤,我们可以高效地解决这个问题,确保时间复杂度的优化。
实现代码
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
int n = nums1.length, m = nums2.length;
Deque<Integer> monotone_stk = new ArrayDeque<>();
Map<Integer, Integer> map = new HashMap<>();
for (int i = m - 1; i >= 0; --i) {
int x = nums2[i];
// 比当前数字小的都不会被选为下一个更大值。
while (!monotone_stk.isEmpty() && monotone_stk.peekLast() <= x) {
monotone_stk.pollLast();
}
// 记录当前数字对应的下一个更大值
map.put(x, monotone_stk.isEmpty() ? -1 : monotone_stk.peekLast());
monotone_stk.addLast(x);
}
int[] ans = new int[n];
for (int i = 0; i < n; ++i) {
ans[i] = map.get(nums1[i]);
}
return ans;
}
}
总结
通过上述实现,我们可以高效地解决“下一个更大元素 I”的问题。这个方法利用了单调栈的特性,确保了时间复杂度的优化。希望这篇博客能帮助你更好地理解这个算法问题和其解决方案。
本文介绍了如何使用单调栈和哈希表在Java中解决LeetCode496问题,通过逆序遍历、移除栈顶元素和记录对应关系,实现O(nums1.length+nums2.length)的时间复杂度。

368

被折叠的 条评论
为什么被折叠?



