图解单调栈,一个例子带你弄懂原理

文章讲述了如何利用单调栈解决两个不同版本的编程题:给定无重复和可能重复元素的数组,找到每个位置左右两边最近且小于/大于当前元素的位置。通过设计保证栈中元素递增的栈实现,文章提供了详细图解和C++代码示例。
摘要由CSDN通过智能技术生成

最近在学栈,看到一个挺经典的题,思路有些曲折,特自写图解。

题目

题目一

给定一个不含重复值的数组arr,找到每一个i位置左边和右边离i位置最近且值比 arr[i] 小的位置,返回所有位置相应的信息。(-1表示不存在)

题目二

给定一个可能含有重复值的数组arr,找到每一个i位置左边和右边离i位置最近且值比 arr[i] 小的位置,返回所有位置相应的信息。(-1表示不存在)

题目要求

时间复杂度为O(N)

核心思想

二者共同的核心

设计一个存放元素为数组位置的栈,保证stack从栈底到栈顶严格递增。

如果能保证,则放入元素;如果不能保证,则弹出栈顶元素。

对弹出的元素来说,当前所遍历到的位置就是右边最近的更小元素,栈中位于弹出元素下面相邻位置的就是左边最近的更小元素。左右无满足此条件的元素时,则满足题意的元素不存在。

若遍历结束后,栈中还存在元素,则对栈中元素进行弹出清算。

有重复值时需要做出的变化

值相同时仍入栈;当出栈时,相邻的相同元素同时出栈。

结果应保留最晚加入的那个。

举个栗子

arr = { 3,4,1,5,6,2,7};

图中,

  • []表示位置,也就是数组下标
  • ()表示元素值,也就是存放的数值是多少

黑色表示挨个放入的流程,蓝色表示放入当前元素后带来的变化,并且在有结果时,把结果标注在了栈下面。

图解流程(无重复)

图解流程(有重复)

例如,arr[] = {3,1,3,4,3,5,3,2,2}

代码怎么写

无重复值

#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;

int* function(int* arr,int l){
    static int* ans = new int[2*l];
    stack<int> s;
    for(int i = 0;i < l;i++){
        while(!s.empty() && arr[s.top()] > arr[i]){
            int popIndex = s.top();
            s.pop();
            int leftLessIndex = s.empty()? -1 : s.top();
            ans[2*popIndex] = leftLessIndex;
            ans[2*popIndex+1] = i;
        }
        s.push(i);
    }

    while(!s.empty()){
        int popIndex = s.top();
        s.pop();
        int leftLessIndex = s.empty()? -1 : s.top();
        ans[2*popIndex] = leftLessIndex;
        ans[2*popIndex+1] = -1;
    }
    return ans;
}

//测试的数据就是例子里的数据
int main(){
    int arr[] = {3,4,1,5,6,2,7};
    int* ans = function(arr,7);
    for(int i = 0;i < 14;i+=2){
        cout<<ans[i]<<"\t"<<ans[i+1]<<"\n";
    }
    return 0;
}

控制台输出与上文推导结果相同

注:由于C++写二维数组比较麻烦,所以采用一维数组的存储,最后按二维数组的格式进行输出。

有重复值

可以考虑把栈中每一层存的元素从int变成vector<int>,相同的值存在同一层。

变式题

把“找到每一个i位置左边和右边离i位置最近且值比 arr[i] 小的位置”改成值更大

一些应用

看看大佬的总结吧

[数据结构]——单调栈-CSDN博客

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值