单调栈和循环单调栈,(附源码)

本篇文章的读者是初学者,旨在帮助他们了解单调栈,同时也检验自己的水平,从而共同提高代码水平和效率,共勉之。
本文为读者详细介绍了单调栈和循环单调栈算法。

单调栈

单调栈(Monotonic Stack)是一种数据结构,通常用于解决一些与单调性相关的问题,例如找到数组中每个元素的下一个更大元素或下一个更小元素等。这种栈的特点是栈内元素保持单调性,可以是递增或递减的。

在单调栈中,元素入栈时,会影响栈内元素的单调性,不满足单调性的元素将会被弹出。这样,栈内的元素就能反映一些与单调性相关的信息。

下面是单调栈的一些基本应用:

  1. 寻找下一个更大元素:
    • 给定一个数组,对于每个元素,找到数组中下一个比它更大的元素。这个问题可以使用单调递减栈来解决。
  2. 寻找下一个更小元素:
    • 类似于上述问题,只不过这次是找下一个更小的元素。这可以使用单调递增栈来解决。
  3. 求解柱状图中的最大矩形面积:
    • 给定一个柱状图,找到其中最大的矩形面积。这可以通过单调递增栈来实现。
  4. 窗口内的最大值和最小值:
    • 在一个滑动窗口中,找到窗口内的最大值或最小值。这可以通过维护一个单调递增或递减的栈来实现。
#include <iostream>
#include <vector>
#include <stack>

using namespace std;

vector<int> nextGreaterElement(const vector<int>& nums) {
    int n = nums.size();
    vector<int> result(n, -1);
    stack<int> s;

    for (int i = 0; i < n; ++i) {
        while (!s.empty() && nums[i] > nums[s.top()]) {
            result[s.top()] = nums[i];
            s.pop();
        }
        s.push(i);
    }

    return result;
}

int main() {
    vector<int> nums = {4, 2, 10, 1, 5, 6};
    vector<int> result = nextGreaterElement(nums);

    cout << "Next greater elements: ";
    for (int num : result) {
        cout << num << " ";
    }

    return 0;
}

  1. 初始状态: 数组为 {4, 2, 10, 1, 5, 6},栈为空,结果数组初始化为 { -1, -1, -1, -1, -1, -1 }

  2. 处理元素 4:

    • 元素 4 入栈,栈变为 { 0 }
  3. 处理元素 2:

    • 元素 2 比栈顶元素 4 小,入栈,栈变为 { 0, 1 }
  4. 处理元素 10:

    • 元素 10 大于栈顶元素 2 和 4,所以更新栈顶元素对应的结果数组值,并继续弹出栈顶元素,直到栈为空或者栈顶元素大于等于 10。结果数组变为 { 10, 10, -1, -1, -1, -1 },栈变为 { 2 }
  5. 处理元素 1:

    • 元素 1 比栈顶元素 10 小,入栈,栈变为 { 2, 3 }
  6. 处理元素 5:

    • 元素 5 大于栈顶元素 1,所以更新栈顶元素对应的结果数组值,并继续弹出栈顶元素,直到栈为空或者栈顶元素大于等于 5。结果数组变为 { 10, 10, -1, 5, -1, -1 },栈变为 { 2, 4 }
  7. 处理元素 6:

    • 元素 6 大于栈顶元素 5,所以更新栈顶元素对应的结果数组值,并继续弹出栈顶元素,直到栈为空或者栈顶元素大于等于 6。结果数组变为 { 10, 10, -1, 5, 6, -1 },栈变为 { 2, 5 }
  8. 最终结果:

    • 栈中剩余的元素 5 没有下一个更大元素,所以更新对应的结果数组值为 -1。最终的结果数组为 { 10, 10, -1, 5, 6, -1 }

    循环单调栈

    循环单调栈(Circular Monotonic Stack)是单调栈的一种变体,专用于解决循环数组相关的问题。循环数组是指数组的末尾与开头相连,形成一个环状结构。在处理循环数组时,我们通常需要考虑元素的相对位置,而循环单调栈正是用来应对这类情况的。

    循环单调栈的基本思想与普通单调栈相似,不同之处在于它处理循环的特殊性。在循环单调栈中,当遍历到数组的最后一个元素时,会再次回到数组的起始位置。因此,在处理循环数组时,我们可以模拟两遍遍历,以处理循环的影响。

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

using namespace std;

vector<int> nextGreaterElements(const vector<int>& nums) {
    int n = nums.size();
    vector<int> result(n, -1);  // 初始化结果数组,全部设置为 -1
    stack<int> s;  // 单调递减栈,栈内存储数组元素的索引

    // 对循环数组进行两轮遍历
    for (int i = 0; i < 2 * n; ++i) {
        int num = nums[i % n];  // 获取数组元素,通过取余来实现循环
        // 当栈非空且当前元素大于栈顶元素时,表示找到了栈顶元素的下一个更大元素
        while (!s.empty() && num > nums[s.top()]) {
            result[s.top()] = num;  // 将结果数组中对应位置设置为当前元素的值
            s.pop();  // 弹出栈顶元素,继续判断
        }
            s.push(i % n);  // 将当前元素的索引入栈(只需要对前 n 个元素入栈)
        
    }

    return result;
}

int main() {
    vector<int> nums = {4, 2, 10, 1, 5, 3};
    vector<int> result = nextGreaterElements(nums);

    cout << "Next greater elements: ";
    for (int value : result) {
        cout << value << " ";
    }

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值