文章目录
- Tag
- 题目来源
- 题目解读
- 解题思路
- 方法一:暴力枚举
- 方法二:单调栈+哈希表
- 知识回顾
- 单调栈
- 写在最后
Tag
【单调栈+哈希表】【数组】
题目来源
496. 下一个更大元素 I
题目解读
找出 nums1
中数字 x
在 nums2
中的位置,并找出在 nums2
中比 x
右侧的第一个比 x
大的元素。
解题思路
方法一:暴力枚举
本题的数据规模为 1 0 3 10^3 103,暴力枚举的时间复杂度为 1 0 6 10^6 106,暴力的方法可以通过。
两层循环,第一层枚举数组 nums1
中的 x
:
- 第二层枚举,先使用
while
循环找到在nums2
中的x
; - 再在
x
的右侧找比x
大的第一个元素,找不到就将-1
作为答案。
实现代码
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
vector<int> res(m, -1);
for (int i = 0; i < m; ++i) {
int j = 0;
int x = nums1[i];
while (j < n && nums2[j] != x) {
++j;
}
for (int k = j + 1; k < n; ++k) {
if (nums2[k] > x) {
res[i] = nums2[k];
break;
}
}
}
return res;
}
};
复杂度分析
时间复杂度:
O
(
m
n
)
O(mn)
O(mn),
m
m
m 为数组 nums1
的长度,
n
n
n 为数组 nums2
的长度。
空间复杂度:
O
(
1
)
O(1)
O(1),作为返回答案的数组 res
不算作额外的空间。
方法二:单调栈+哈希表
我们可以计算 nums2
中的每一个元素右侧的更大元素,然后将元素与更大的元素存储在哈希表 hashMap
中,最后遍历 nums1
中的 x
,查找 x
在哈希表中对应的值,这样查询的时间复杂度为
O
(
m
)
O(m)
O(m),
m
m
m 为数组 nums1
的长度。
计算 nums2
中的每一个元素右侧的更大元素,我们可以使用暴力枚举的方法,这样的时间复杂度为
O
(
n
2
)
O(n^2)
O(n2),
n
n
n 为数组 nums2
的长度,也有时间复杂度为
O
(
n
)
O(n)
O(n) 的方法。
O
(
n
2
)
O(n^2)
O(n2) 计算 hashMap
暴力枚举的方法很简单,直接上代码。
int n = nums2.size();
unordered_map<int, int> hashMap;
for (int i = 0; i < n; ++i) {
int j = i + 1;
// 找到大于nums1[i] 的第一个元素或者越界
while (j < n && nums2[j] <= nums1[i]) {
++j;
}
// 如果越界了,更新值为 -1,否则表示找到了大于 nums1[i] 的第一个元素
hashMap[nums1[i]] = j < n ? nums2[j] : -1;
}
O
(
n
)
O(n)
O(n) 计算 hashMap
维护一个单调栈 stk
,栈中元素值从栈底到栈顶是依次递增的。
我们从后向前枚举数组 nums2
来更新 hashMap
以及单调栈 stk
(也可以从前往后枚举数组来更新 hashMap
,后面会贴上代码):
- 将当前枚举的元素值
nums2[i]
与栈顶的元素比较,如果栈非空并且栈顶的元素值小于或者等于nums2[i]
,就出栈,直到栈为空或者找到比nums2[i]
的栈中元素; - 如果栈为空了,说明
nums2[i]
右侧没有比它大的元素,更新哈希表hashMap[num2[i]] = -1
;否则就是找到了nums2[i]
右侧比它大的元素了; - 将
nums2[i]
加入栈中。
实现代码如下:
unordered_map<int, int> hashMap; // 存放元素的下一个更大元素
for(i = nums2.size()-1; i >= 0; --i){
int num = nums2[i];
while(!stk.empty() && num >= stk.top()){
stk.pop();
}
hashMap[num] = stk.empty() ? -1 : stk.top();
stk.push(num);
}
O
(
n
)
O(n)
O(n) 时间复杂度更新 hashMap
的方法以及哈希表更新答案数组 ans
的方法是本题最优的方法,总的实现代码如下:
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
unordered_map<int, int> hashMap; // 存放元素的下一个更大元素
stack<int> stk;
int i;
int n = nums1.size();
vector<int> ans(n);
for(i = nums2.size()-1; i >= 0; --i){
int num = nums2[i];
while(!stk.empty() && num >= stk.top()){
stk.pop();
}
hashMap[num] = stk.empty() ? -1 : stk.top();
stk.push(num);
}
for(i = 0; i < n; ++i){
ans[i] = hashMap[nums1[i]];
}
return ans;
}
};
从后向前枚举更新 hashMap
直接贴上代码
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
unordered_map<int, int> hashtable; // 存放对应元素的下一个更大元素
stack<int> stk;
for(int i = 0; i < nums2.size(); ++i){
int num = nums2[i];
while(!stk.empty() && stk.top() < num){
hashtable[stk.top()] = num;
stk.pop();
}
stk.push(num);
}
int n = nums1.size();
vector<int> ans(n, -1);
for(int i = 0; i < n; ++i){
if (hashtable.find(nums1[i]) != hashtable.end()) {
ans[i] = hashtable[nums1[i]];
}
}
return ans;
}
};
复杂度分析
时间复杂度:
O
(
m
+
n
)
O(m+n)
O(m+n),
m
m
m 为数组 nums1
的长度,
n
n
n 为数组 nums2
的长度。 我们需要遍历 nums2
以计算 nums2
中每个元素右边的第一个更大的值,对应的时间复杂度为
O
(
n
)
O(n)
O(n);需要遍历 nums1
以生成查询结果,对应的时间复杂度为
O
(
m
)
O(m)
O(m)。
空间复杂度: O ( n ) O(n) O(n),使用哈希表的额外空间开销。
知识回顾
单调栈
栈是一种比较常用的基本数据结构,我们使用栈的性质可以解决一些 “后进先出” 的问题。
有别于栈的是,单调栈里的元素是有序的。单调栈按栈内元素的递增、递减顺序分为单调递增栈和单调递减栈。但是查询了一些资料之后没有发现明确的单调递增栈(单调递减栈)定义。有的是说,从栈底到栈顶的元素按照递增次序的是单调递增栈,而有的说法相反。这里也就不纠结了,也没有一个官方的说法。自己在使用的时候自己明确就好,有的题目要求使用到的话题目会说清楚的。
【每日一题】股票价格跨度
【单调栈】下一个更大元素 II
【单调栈】下一个更大元素 III
通过本题以及以上几个例题的学习,我们进行一些小小的总结:下一个/上一个更大、更小的值,基本上都可以使用【单调栈】来解决。
写在最后
如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。
如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。
最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 👍 哦。