0
点赞
收藏
分享

微信扫一扫

【代码随想录】【LeetCode】自学笔记 12 -单调栈

伢赞 2022-04-03 阅读 110
c++leetcode

前言

什么时候用单调栈呢?通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。
时间复杂度为 O ( n ) O(n) O(n)
单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素大或小的元素,优点是只需要遍历一次。

739. 每日温度

/*典型的单调栈的模板!!!
那result[6] , result[7]怎么没更新啊,元素也一直在栈里。
其实定义result数组的时候,就应该直接初始化为0,如果result没有更新,说明这个元素右面没有更大的了,也就是为0。
情况一:当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
情况二:当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
情况三:当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
*/
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& T) {
        // 递增栈
        stack<int> st;
        vector<int> result(T.size(), 0);
        st.push(0);
        for (int i = 1; i < T.size(); i++) {
            if (T[i] <= T[st.top()])  st.push(i);           // 情况一
                
             else {
                while (!st.empty() && T[i] > T[st.top()]) { // 情况三--while
                    result[st.top()] = i - st.top();
                    st.pop();//
                }
                st.push(i);///
            }
        }
        return result;
    }
};

496. 下一个更大元素 I

/*
可以看出最后是要求nums1的每个元素在nums2中下一个比当前元素大的元素,那么就要定义一个和nums1一样大小的数组result来存放结果。
result数组如果某位置没有被赋值,那么就应该是是-1,所以就初始化为-1。
没有重复元素,我们就可以用map来做映射了。根据数值快速找到下标,还可以判断nums2[i]是否在nums1中出现过。
C++中,当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的。
使用单调栈,首先要想单调栈是从大到小还是从小到大。本题和739. 每日温度是一样的。
栈头到栈底的顺序,要从小到大,也就是保持栈里的元素为递增顺序。只要保持递增,才能找到右边第一个比自己大的元素。

如下三种情况,一定要分析清楚。

情况一:当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
此时满足递增栈(栈头到栈底的顺序),所以直接入栈。

情况二:当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
如果相等的话,依然直接入栈,因为我们要求的是右边第一个比自己大的元素,而不是大于等于!

情况三:当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
此时如果入栈就不满足递增栈了,这也是找到右边第一个比可能比 nums1 自己大的元素的时候。

判断栈顶元素是否在nums1里出现过,(注意栈里的元素是nums2的元素),如果出现过,开始记录结果。
(个人理解:维持n2 (i--顺序的)单调栈为主,找n1 (在n2 中的)第一个大为辅。。。)

记录结果这块逻辑有一点小绕,要清楚,此时栈顶元素在nums2中右面第一个大的元素是nums2[i]即当前遍历元素。

*/
class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        stack<int> st;
        vector<int> result(nums1.size(), -1);//nums1.size() 可以为0,见下
        if (nums1.size() == 0) return result;

        unordered_map<int, int> umap; // key:下标元素,value:下标///<int, int>/这个umap 大小最大为n1里元素的最大值!
        for (int i = 0; i < nums1.size(); i++) {
            umap[nums1[i]] = i;
        }
        st.push(0);//st 是记录下标的///(初始化,已经录入了第一个)
        for (int i = 1; i < nums2.size(); i++) {      //i 从1(第二个开始)
            if (nums2[i] <= nums2[st.top()]) {           // 情况一
                st.push(i);
            } 
            // else if (nums2[i] == nums2[st.top()]) {   // 情况二
            //     st.push(i);
            // } 
            else {                                    // 情况三
                while (!st.empty() && nums2[i] > nums2[st.top()]) {
                    if (umap.count(nums2[st.top()]) > 0) { //【第一次出现count】 看map 里是否存在这个元素【若存在,值>0】
                        int index = umap[nums2[st.top()]]; // 根据map找到nums2[st.top()] 在 nums1中的下标
                        result[index] = nums2[i];
                    }
                    st.pop();
                }
                st.push(i);/若为空,则push
            }
        }
        return result;
    }
};

503. 下一个更大元素 II

// 版本一讲两个nums数组拼接在一起,使用单调栈计算出每一个元素的下一个最大值,最后再把结果集即result数组resize到原数组大小就可以了。
//其实也可以不扩充nums,而是在遍历的过程中模拟走了两边nums。
class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        vector<int> result(nums.size(), -1);
        if (nums.size() == 0) return result;
        stack<int> st;
        for (int i = 0; i < nums.size() * 2; i++) {
            // 模拟遍历两边nums,注意一下都是用i % nums.size()来操作
            while (!st.empty() && nums[i % nums.size()] > nums[st.top()]) {
                result[st.top()] = nums[i % nums.size()];
                st.pop();
            }
            st.push(i % nums.size());
        }
        return result;
    }
};
举报

相关推荐

0 条评论