C++单调栈、单调队列笔记

单调栈和单调队列

  • 单调栈
    • 496. 下一个更大元素 I
    • 503. 下一个更大元素 II
    • 739. 每日温度
      • 单调栈模板
  • 单调队列
    • 239. 滑动窗口最大值

单调栈

496. 下一个更大元素 I

给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。

请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。

示例 1:

输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。
示例 2:

输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

提示:

1 <= nums1.length <= nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 104
nums1和nums2中所有整数 互不相同
nums1 中的所有整数同样出现在 nums2 中

单调栈代码:

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        stack<int> s;
        unordered_map<int,int> res;//存对应nums2的next greater number问题的结果数组,后面对于nums1直接取数,然后读
        vector<int>ans;
        int n = nums1.size(), m = nums2.size();
        for (int i = m - 1; i >= 0; --i)
        {//倒着往栈里面加数据,加进去的应该是从上到下的一个单调递增的序列
            while (!s.empty() && s.top() <= nums2[i])//如果这个新加进来的数据比之前加进来的大,那么弹出之前加进来的,只有最大的才能在下面
                s.pop();
            res[nums2[i]] = s.empty() ? -1 : s.top();
            s.push(nums2[i]);
        }
        for (int i = 0; i < n; i++)
            ans.push_back(res.at(nums1[i]));
        return ans;
    }
};

503. 下一个更大元素 II

给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。

示例 1:

输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        int m = nums.size();
        for (int i = 0; i < m; i++)nums.push_back(nums[i]);
        //把循环数组实现,连接一个,就和正常的单调栈一样了,但是这个方法不好
        vector<int>res(2*m);
        vector<int>ans;
        stack<int>s;
        for (int i = 2*m - 1; i >= 0; --i)
        {
            while (!s.empty() && s.top() <= nums[i])
                s.pop();
            res[i] = s.empty() ? -1 : s.top();
            s.push(nums[i]);
        }
        for (int i = 0; i < m; i++)
            ans.push_back(res[i]);
        return ans;
    }
};

方法2

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        int n = nums.size();
        vector<int> ret(n, -1);
        stack<int> stk;
        for (int i = 0; i < n * 2 - 1; i++) {
            //顺序入栈的方法,这样不必显式做到连接两次数组,可以用取余的方法
            while (!stk.empty() && nums[stk.top()] < nums[i % n]) {
                ret[stk.top()] = nums[i % n];
                stk.pop();
            }
            stk.push(i % n);
        }
        return ret;
    }
};

739. 每日温度

请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

vector<int> dailyTemperatures(vector<int>& T) {
    vector<int> res(T.size());
    // 这里放元素索引,而不是元素
    stack<int> s;
    /* 单调栈模板 */
    for (int i = T.size() - 1; i >= 0; i--) {
        while (!s.empty() && T[s.top()] <= T[i])
            s.pop();
        // 得到索引间距,索引是从大到小放的所以可以直接-i
        res[i] = s.empty() ? 0 : (s.top() - i);
        // 将索引入栈,而不是元素,索引入栈,好对比距离
        s.push(i);
    }
    return res;
}

单调栈模板

1、倒序入栈

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        stack<int> s;
        unordered_map<int,int> res;//存对应nums2的next greater number问题的结果数组,后面对于nums1直接取数,然后读
        vector<int>ans;
        int n = nums1.size(), m = nums2.size();
        for (int i = m - 1; i >= 0; --i)
        {//倒着往栈里面加数据,加进去的应该是从上到下的一个单调递增的序列
            while (!s.empty() && s.top() < nums2[i])//如果这个新加进来的数据比之前加进来的大,那么弹出之前加进来的,只有最大的才能在下面
                s.pop();
            //在这里的时候,相当于每遍历一个i就可以得到一个结果
            //因为是知道后面有没有比自己大的,但是顺序遍历不可以
            res[nums2[i]] = s.empty() ? -1 : s.top();
            s.push(nums2[i]);
        }
        for (int i = 0; i < n; i++)
            ans.push_back(res.at(nums1[i]));
        return ans;
    }
};

2、顺序入栈

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        int n = nums.size();
        vector<int> ret(n, -1);
        stack<int> stk;
        for (int i = 0; i < n - 1; i++) {
            while (!stk.empty() && nums[stk.top()] < nums[i]) {
                //顺序插入栈的话就必须是要在栈里面存放下标,
                //因为逆序的话相当于后面的结果是先知道的,
                //所以到了某个值,是一定知道后面有没有比自己大的,但是正序是不知道的
                ret[stk.top()] = nums[i];
                stk.pop();
            }
            stk.push(i);
        }
        return ret;
    }
};

单调队列

239. 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
示例 2:

输入:nums = [1], k = 1
输出:[1]
示例 3:

输入:nums = [1,-1], k = 1
输出:[1,-1]
示例 4:

输入:nums = [9,11], k = 2
输出:[11]
示例 5:

输入:nums = [4,-2], k = 2
输出:[4]

提示:

1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length

class MonotonicQueue {
    //实现一个单调队列
private:
    deque<int>data;
public:
    void push(int n) {//做的是单调队列的插入操作,单调队列是想要一个单调不增的数值
        while (!data.empty() && data.back() < n)//如果新来的数字要大于队列中最后一个
            data.pop_back();//就把最后一个弹出
        data.push_back(n);//无论如何要把n放进来
    }
    int max() {
        return data.front();
    }
    void pop(int n) {//弹出队头元素,就是弹出滑动窗口最开始的点
        if (!data.empty() && data.front() == n)
            data.pop_front();
    }
};


class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        MonotonicQueue window;
        vector<int>res;
        for (int i = 0; i < nums.size(); i++)
        {
            if (i < k - 1)
                window.push(nums[i]);//先把窗口放k-1个数字
            else {
                window.push(nums[i]);//再放一个,不要纠结滑动窗口的长度,放一个弹出一个,不会有事的
                res.push_back(window.max());//放最大值,取出来,放到结果
                window.pop(nums[i - k + 1]);//弹出队头,因为要弹出的数字是距离i为k-1个位置
            }
        }
        return res;
    }
};

你可能感兴趣的:(C++从入门到...)