【单调栈】下一个更大元素 I

Tag

【单调栈+哈希表】【数组】


题目来源

496. 下一个更大元素 I


题目解读

找出 nums1 中数字 xnums2 中的位置,并找出在 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

通过本题以及以上几个例题的学习,我们进行一些小小的总结:下一个/上一个更大、更小的值,基本上都可以使用【单调栈】来解决。

写在最后

如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。

如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。

最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 👍 哦。

  • 18
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wang_nn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值