单调栈算法(C++版)

单调栈模式使用栈来维护按特定顺序(递增或递减)排列的元素序列。即,要么栈空,要么栈内所有数字都是有序的,用于特定的运算。

方法:顺序Or逆序遍历(看情况),以下是下大上小的情况,逆序遍历

1.栈空或栈顶元素 top.value 比当前元素 x 大的时候,x 可以进栈,此时记下 x 进栈前的栈顶元素top.value作为x 的下一个更大元素(此时应该有哈希表);

2.栈顶元素 top.value比当前 x 还要小的时候,先清栈,直到栈顶元素比 x 大或者栈空停止清栈操作,此时的栈顶元素top.value 就是 x 的下一个更大元素(空了就是-1)(此时用哈希表记录比较方便)。

动态过程:自己动手画一遍或者看官方解。


496. 下一个更大元素 I - 力扣(LeetCode)

【Leetcode496】下一个更大元素

nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。

例子:输入:nums1 = [4,1,2], nums2 = [1,3,4,2]. 输出:[-1,3,-1]

思路与转化:

这里就不说暴力求解了,用单调栈+哈希表进行运算。这个题利用num1作为查询数组,主要是对num2进行处理。所以我们的代码分成了两块,重点是对num2进行处理。思路通了之后,就上代码了。

#include <iostream>
#include <vector>
#include <stack>
#include <unordered_map>
using namespace std;

// 函数用于寻找数组中每个元素的下一个更大元素
unordered_map<int, int> nextGreaterElement(const vector<int>& num2) {
    stack<int> st; // 单调栈
    unordered_map<int, int> result; // 存储结果的哈希表

    // 从右向左遍历数组
    for (auto it = num2.rbegin(); it != num2.rend(); ++it) {
        int num = *it;
        // 当栈不为空且栈顶元素小于等于当前元素时,弹出栈顶元素
        while (!st.empty() && st.top() <= num) {
            st.pop();
        }
        // 如果栈不为空,栈顶元素就是当前元素的下一个更大元素
        if (!st.empty()) {
            result[num] = st.top();
        } 
        // 如果栈为空,说明当前元素没有下一个更大元素,记为 -1
        else {
            result[num] = -1;
        }
        // 将当前元素压入栈中
        st.push(num);
    }
    return result;
}

int main() {
    vector<int> num1 = {2, 4};
    vector<int> num2 = {4, 5, 2, 25};
    unordered_map<int, int> resultMap = nextGreaterElement(num2);

    vector<int> output; // 用于存储最终输出结果的数组
    for (int num : num1) {
        output.push_back(resultMap[num]);
    }

    // 输出结果数组
    cout << "[";
    for (int i = 0; i < output.size(); ++i) {
        if (i > 0) {
            cout << ", ";
        }
        cout << output[i];
    }
    cout << "]" << endl;

    return 0;
}

【Leetcode739】每日温度

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

例子:

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

分析一下吧,还是右至左遍历,

1.检测到栈顶温度大于等于当前温度,记录1,入栈;

2.检测到顶温度小于当前温度,出栈直至栈顶温度大于等于当前温度。若此时栈不为空,记录【出栈次数+1】作为当前日期的下一个更高温天数,入栈;若空了,记录0。(有错误,有时忽略了栈中没有却实际存在的温度值,比如遍历到75时,栈中有71、72、76,若按照这种思路会忽略69度的这一天)

所以,我们应该记录的是原数组的索引值,利用栈顶温度索引与当前温度索引的差值直接求出...

1.针对每个位置 i,当栈不为空且栈顶索引对应的温度小于等于当前位置 i 的温度时,进行出栈操作。
2.若出栈操作结束后栈不为空,表明存在比当前温度更高的温度,当前位置 i 距离下一个更高温的天数为 st.top() - i(即栈顶索引与当前索引的差值);若栈为空,意味着不存在比当前温度更高的温度,当前位置 i 距离下一个更高温的天数记为 0。
3.将当前位置的索引 i 压入栈中。

思路和代码:依旧是单调栈+哈希表的组合

#include <iostream>
#include <vector>
#include <stack>
using namespace std; 
vector<int> dailyTem(const vector<int>& tem) {
    int n = tem.size();
    vector<int> result(n, 0);
    stack<int> st;

    // 从右至左遍历温度数组
    for (int i = n - 1; i >= 0; --i) {
        // 当栈不为空且栈顶温度小于等于当前温度时,出栈
        while (!st.empty() && tem[st.top()] <= tem[i]) {
            st.pop();
        }
        if (!st.empty()) {
            // 栈不为空,记录下一个更高温度出现的天数
            result[i] = st.top() - i;
        } else {
            // 栈为空,记录 0
            result[i] = 0;
        }
        // 将当前位置索引入栈
        st.push(i);
    }
    return result;
}

int main() {
    vector<int> tem = {73, 74, 75, 71, 69, 72, 76, 73};
    vector<int> output = dailyTem(tem);

    cout << "[";
    for (int i = 0; i < output.size(); ++i) {
        if (i > 0) {
            cout << ", ";
        }
        cout << output[i];
    }
    cout << "]" << endl;

    return 0;
}

【Leetcode84】柱状图中最大的矩形

给定n个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

有点像最大盛水量,双指针算法的那个题,但分析一下之后发现又不一样。这个题是只有柱子所围成的矩形区域,不包含柱子上的空间。

初步分析:每个柱子作为中心,左右找比他高的或者跟他一样高的连续的柱子,所延伸的左右长度就是以该柱子为基准点的面积最大值,记下面积最大值和对应的下标范围。最后,找出这些所有不同柱子基准点时的面积最大值。所以核心就是确定边界位置,左右找到比基准点小的值的索引或者数组边界即可。

思路与算法:1.暴力求解
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int largestRectangleArea(vector<int>& heights) {
    int n = heights.size();
    int max_area = 0;
    // 遍历每个柱子作为基准点
    for (int i = 0; i < n; ++i) {
        // 初始化左右边界
        int left = i;
        int right = i;
        // 向左扩展,找到第一个比基准点矮的柱子
        while (left > 0 && heights[left - 1] >= heights[i]) {
            left--;
        }
        // 向右扩展,找到第一个比基准点矮的柱子
        while (right < n - 1 && heights[right + 1] >= heights[i]) {
            right++;
        }
        // 计算以当前柱子为基准的矩形面积
        int width = right - left + 1;
        int area = heights[i] * width;
        // 更新最大面积
        max_area = max(max_area, area);
    }
    return max_area;
}

int main() {
    vector<int> heights = {2, 1, 5, 6, 2, 3};
    cout << largestRectangleArea(heights) << endl;
    return 0;
}

思路与算法:2单调栈

从左到右依次遍历新数组中的每个柱子,对于当前遍历到的柱子:

1.栈为空或当前柱子高度大于等于栈顶柱子高度时,直接将当前柱子的索引压入栈中。

2.当前柱子高度小于栈顶柱子高度时,说明找到了栈顶柱子右侧第一个高度小于它的柱子。

弹出栈顶元素,记为 top_index,该元素对应的柱子高度即为当前要计算面积的矩形的高 h。
计算矩形的宽度 w:由于栈内元素是单调递增的,弹出栈顶元素后,新的栈顶元素对应的柱子就是原栈顶柱子左侧第一个高度小于它的柱子,所以矩形的宽度为当前索引 i 减去新栈顶元素的索引再减 1,即 w = i - st.top() - 1。计算以当前柱子为高的矩形面积 area = h * w。
更新最大面积 maxArea:将计算得到的矩形面积 area 与当前的最大面积 maxArea 进行比较,取较大值更新 maxArea。
重复上述操作,直到栈为空或者栈顶柱子高度小于等于当前柱子高度,然后将当前柱子的索引压入栈中。

#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>

using namespace std;

int largestRectangleArea(vector<int>& heights) {
    // 创建新的数组,避免修改原数组
    vector<int> new_heights(heights);
    new_heights.insert(new_heights.begin(), 0);
    new_heights.push_back(0);

    stack<int> st;
    int max_area = 0;

    for (int i = 0; i < new_heights.size(); ++i) {
        while (!st.empty() && new_heights[st.top()] > new_heights[i]) {
            int h = new_heights[st.top()];
            st.pop();
            int w = i - st.top() - 1; // 计算宽度
            max_area = max(max_area, h * w);
        }
        st.push(i);
    }

    return max_area;
}

int main() {
    vector<int> heights = {2, 1, 5, 6, 2, 3};
    cout << largestRectangleArea(heights) << endl;
    return 0;
}
对于这个算法的详细解释:单调栈补充(C++版)-CSDN博客
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值