第十章 单调栈part01

单调栈运行过程

需求:在数组temperatures(T):[73,74,75,71,69,72,76,73]中,对于数组中的每个元素,从当前元素右边找到第一个比当前元素值更大的元素,将该元素与当前元素的距离保存到一个数组中进行返回。

分析过程:

  • 情况一:当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况

    • 将当前遍历元素插入栈中,作为栈顶元素
  • 情况二:当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况

    • 将当前遍历元素插入栈中,作为栈顶元素
  • 情况三:当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况

    • 计算当前元素和栈顶元素的下标距离,将结果保存到结果数组中,并将栈顶元素弹出,将当前元素插入栈中

单调栈中保存的均为数组元素的下标值,模拟单调栈运行过程如下:

第一步:i = 0, temperatures[i] = 73

  • 栈为空,将73对应的索引0插入栈中,作为栈顶元素

    image

  • 此时:stack = [0]

第二步:i = 1, temperatures[i] = 74

  • 当前温度 74 > 栈顶温度 73,满足情况三。

    image

  • 弹出栈顶索引 0,更新 result[0] = 1 - 0 = 1,将索引 1 压入栈。

    image

  • 此时:result = [1, 0, 0, 0, 0, 0, 0, 0]、stack = [1]

第三步:i = 2, temperatures[i] = 75

  • 当前温度 75 > 栈顶温度 74,满足情况三。

    image

  • 弹出栈顶索引 1,更新 result[1] = 2 - 1 = 1,将索引 2 压入栈。

    image

  • 此时:result = [1, 1, 0, 0, 0, 0, 0, 0]、stack = [2]

第四步:i = 3, temperatures[i] = 71

  • 当前温度 71 < 栈顶温度 75,满足情况一,直接将索引 3 压入栈

    image

  • 此时:stack = [2, 3]

第五步:i = 4, temperatures[i] = 69

  • 当前温度 69 < 栈顶温度 71,满足情况一,直接将索引 4 压入栈

    image

  • 此时:stack = [2, 3, 4]

第六步:i = 5, temperatures[i] = 72

  • 当前温度 72 > 栈顶温度 69,满足情况三

    image

  • 弹出栈顶索引 4,更新 result[4] = 5 - 4 = 1,继续比较发现当前温度 72 > 栈顶温度 71,满足情况三

image

  • 弹出栈顶索引 3,更新 result[3] = 5 - 3 = 2,将索引 5 压入栈。

    image

  • 此时:result = [1, 1, 0, 2, 1, 0, 0, 0]、stack = [2, 5]

第七步:i = 6, temperatures[i] = 76

  • 当前温度 76 > 栈顶温度 72,满足情况三。

    image

  • 弹出栈顶索引 5,更新 result[5] = 6 - 5 = 1,继续遍历当前温度 76 > 栈顶温度 75,满足情况三。

    image

  • 弹出栈顶索引 2,更新 result[2] = 6 - 2 = 4,将索引 6 压入栈。

    image

  • 此时:result = [1, 1, 4, 2, 1, 1, 0, 0]、stack = [6]

第八步:i = 7, temperatures[i] = 73

  • 当前温度 73 < 栈顶温度 76,直接将索引 7 压入栈。

    image

  • 此时:stack = [6, 7]、result = [1, 1, 4, 2, 1, 1, 0, 0]

第八步:i = 8, 超出temperatures数组范围,退出循环。

每日温度

题目描述

给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

示例 1:

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]

示例 2:

输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]

示例 3:

输入: temperatures = [30,60,90]
输出: [1,1,0]

解题思路

通过分析题目可知,我们需要依次遍历温度数组,从当前温度右侧找到第一个比当前温度高的值,并将二者的距离保存到一个数组中,然后进行返回,像这种从某个元素的左边或者右边找比当前元素大或者当前元素小的问题,都可以使用单调栈进行解决,具体过程可以参考单调栈运行过程。

代码实现

测试地址:https://leetcode.cn/problems/daily-temperatures/

class Solution {
public:
    vector<int> dailyTemperatures(vector<int> &temperatures) {
        int n = temperatures.size(); // 获取温度数组的长度
        vector<int> result(n, 0); // 初始化结果数组,默认值为0
        stack<int> st; // 创建一个栈,用于存储温度数组的索引

        for (int i = 0; i < n; ++i) { // 遍历温度数组
            while (!st.empty() && temperatures[i] > temperatures[st.top()]) {
                // 当栈不为空且当前温度大于栈顶索引对应的温度时
                int prevIndex = st.top(); // 获取栈顶索引
                result[prevIndex] = i - prevIndex; // 计算天数差并更新结果数组
                st.pop(); // 弹出栈顶元素
            }
            st.push(i); // 将当前索引压入栈中
        }

        return result; // 返回结果数组
    }
};

下一个更大元素 I

题目描述

nums1 中数字 x下一个更大元素 是指 xnums2 中对应位置 右侧第一个x 大的元素。

给你两个 没有重复元素 的数组 nums1nums2 ,下标从 0 开始计数,其中nums1nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j]下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1

返回一个长度为 `nums1.length` 的数组`ans` 作为答案,满足`ans[i]` 是如上所述的 **下一个更大元素** 。

示例 1:

输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
- 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
- 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。

示例 2:

输入:nums1 = [2,4], nums2 = [1,2,3,4].
输出:[3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。
- 4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1 。

解题思路

本题的目标是找到 nums1 中每个元素在 nums2 中的下一个更大元素。如果不存在下一个更大元素,则返回 -1。nums2 中没有重复元素,并且 nums1nums2 的子集。这意味着我们需要在 nums2 中找到 nums1 中每个元素的位置,然后找到其右侧第一个更大的元素。

具体步骤如下:

  1. 初始化数据结构:

    • 创建一个结果向量 result,长度为 nums1 的长度,并初始化为 -1,表示默认所有元素都没有下一个更大元素。
    • 使用一个栈 st 来维护一个从栈顶到栈底的递增序列。
    • 使用一个哈希表 umap 来存储 nums1 中每个元素及其对应的索引,以便快速查找。
  2. 填充哈希表:

    • 遍历 nums1,将每个元素及其对应的索引存入 umap 中。
  3. 遍历 nums2

    • 对于 nums2 中的每个元素,执行以下操作:

      • 维护单调栈: 当栈不为空且当前元素大于栈顶元素时,弹出栈顶元素,表示找到了栈顶元素的下一个更大元素。
      • 更新结果: 如果栈顶元素存在于 nums1 中,通过哈希表获取其索引,并更新 result 中对应位置的值为当前元素。
      • 入栈: 将当前元素的索引入栈,继续处理下一个元素。
  4. 返回结果: 遍历结束后,返回结果向量 result

代码实现

测试地址

class Solution {
public:
    vector<int> nextGreaterElement(vector<int> &nums1, vector<int> &nums2) {
        int len1 = nums1.size();
        int len2 = nums2.size();
        vector<int> result(len1, -1); // 初始化结果向量,默认值为 -1
        stack<int> st; // 定义一个栈用于存储 nums2 的索引
        unordered_map<int, int> umap; // 定义一个哈希表用于存储 nums1 的值及其索引

        // 将 nums1 中的元素及其索引存入哈希表
        for (int i = 0; i < len1; i++) {
            umap[nums1[i]] = i;
        }

        // 遍历 nums2,使用单调栈找到每个元素的下一个更大元素
        for (int i = 0; i < len2; ++i) {
            // 栈不为空且当前元素大于栈顶元素
            while (!st.empty() && nums2[i] > nums2[st.top()]) {
                int top = st.top(); // 获取栈顶元素的索引
                st.pop(); // 弹出栈顶元素
                // 如果栈顶元素在 nums1 中
                if (umap.count(nums2[top]) > 0) {
                    int index = umap[nums2[top]]; // 获取该元素在 nums1 中的索引
                    result[index] = nums2[i]; // 更新 result 中对应位置的值为当前元素
                }
            }
            st.push(i); // 将当前元素的索引入栈
        }

        return result; // 返回结果向量
    }
};

下一个更大元素 II

题目描述

给定一个循环数组 numsnums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素

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

示例 1:

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

示例 2:

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

解题思路

本题目标是在一个循环数组中,对每个元素找到其下一个更大的元素。因为涉及到在元素左右两侧找到更大更小的元素,因此可以采用单调栈进行解题。

具体解题步骤如下:

  1. 初始化数据结构

    • result 数组:存储每个元素的下一个更大元素,初始化为 -1。
    • stack<int> st:存储数组索引而不是元素值。这样可以在遇到更大的元素时快速找到需要更新的 result 的位置。
  2. 处理循环数组

    • 通过循环 2*n 次来模拟循环数组的效果,使用 i % n 来保持索引在数组范围内。
  3. 维护单调栈

    • 栈内存储的索引对应的 nums 值应该为从栈顶到栈底的递增序列。这样,一旦当前元素 nums[i % n] 大于 nums[st.top()],就找到了下一个更大的元素。
    • 每找到一个更大元素,就通过栈顶索引更新 result 数组,并将该栈顶元素弹出。
  4. 循环结束后

    • 返回完整的 result 数组。由于栈中的元素是递增的,如果栈中还有元素,其对应的 result 保留为 -1,表示没有找到更大的元素。

时空分析:

  • 时间复杂度:O(2n),即 O(n)。每个元素最多被压栈和弹栈各一次。
  • 空间复杂度:O(n)。额外空间用于存储栈。

代码实现

测试地址:https://leetcode.cn/problems/next-greater-element-ii/

class Solution {
public:
    vector<int> nextGreaterElements(vector<int> &nums) {
        int n = nums.size();  // 获取数组的长度
        vector<int> result(n, -1);  // 初始化结果数组,各元素初始为 -1(表示没有找到更大的元素)
        stack<int> st;  // 使用一个栈来维护索引,便于查找每个元素的下一个更大元素

        // 循环数组两倍长度,实现循环数组的效果
        for (int i = 0; i < n * 2; i++) {
            // 当栈不为空且当前元素大于栈顶索引所指的元素时,进行处理
            while (!st.empty() && nums[i % n] > nums[st.top()]) {
                int top = st.top();  // 获取栈顶元素索引
                st.pop();  // 弹出栈顶元素
                result[top] = nums[i % n];  // 更新栈顶元素索引对应的结果为当前元素值
            }
            st.push(i % n);  // 将当前索引压入栈中
        }
        return result;  // 返回结果
    }
};
  • 27
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值