高级数据结构-单调栈

单调栈的基本应用:在一串数中,找到左边和右边最近的大于(小于)自己的数,O(n)实现。

以找最近邻大数为例,解法是维护一个单调递增的栈。假设现在有一个单调栈,从栈底至栈顶元素为A、B、C,现在要插入元素D。如果D>C,那么要讲C弹出,同时,C的右值为D,左值为B。遍历完成之后,栈中可能还会剩下一些元素,但是对于这些元素来说,因为压在自己上面的都是比自己小的数,所以他们没有最右近邻值。

理由说明:由插入顺序可知原序列为……B……C……D,如果C的右值在C-D之间,设其为x,那么由于x>C,在插入x的时候就会因为单调性的缘故而将C给弹出栈,也就不会发生D要压在C上面的情况了。同理,若B-C之间有y>B,也不会发生C压在B之上。

代码:

#include<cstdio>
#include<stack>
using namespace std; 
struct node{
    int L,R,v,squence;
};
node result[1000];
int main(){
    stack<node> S;
    int n,index,R=-1;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&result[i].v);
        result[i].squence=i;
    }
    for(int i=1;i<=n;i++){
        while(!S.empty()&&S.top().v<result[i].v){//维持单调性而进行pop 
            index=S.top().squence;
            result[index]=S.top();
            result[index].R=i;
            S.pop();
            if(!S.empty()){
                result[index].L=S.top().squence;
            }
            else result[index].L=-1;
        }
        S.push(result[i]); 
    }
    while(!S.empty()){
        index=S.top().squence;
        result[index]=S.top();
        result[index].R=R;
        S.pop();
        if(!S.empty()){
            result[index].L=S.top().squence;
        }
        else result[index].L=-1;
    }
    for(int i=1;i<=n;i++){
        printf("第%d个:  值:%d   L:%d  R:%d\n",i,result[i].v,result[i].L,result[i].R);
    }
    return 0;
}

缺陷:对于连续的相同值处理不当。这是因为我们现在的单调栈只有严格大于进和严格大于出,并把要进的数当作弹出的数的右边界导致的。这样会使栈在面对栈顶:5   要进栈元素:5的时候,将弹出的5的右边界认为是当前进栈元素5的索引。

解决方案:1.特判相等情况,使得决策推迟,相等让其进栈,例如5 5 5 5 5,然后将所有相等元素的右边界设置为与栈顶最先弹出的5的边界相等。

2.让相等且连续的数的索引压在一个空间里,节点内多加一个vector用来记录相等且连续的值的索引。

代码:

#include<cstdio>
#include<stack>
#include<vector>
using namespace std; 
struct node{
    int L,R,v;
};
struct store{
    int v;
    vector<int>squence;
};
node result[1000];
int main(){
    stack<store> S;
    int n,index,R=-1,len,L;
    bool flag;
    store val;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&result[i].v);
    }
    for(int i=1;i<=n;i++){
        flag=false;
        if(!S.empty()&&S.top().v<=result[i].v) {
            while(!S.empty()&&S.top().v<=result[i].v){//维持单调性而进行pop 
                if(S.top().v==result[i].v){//等于的时候压进一个vector空间 
                    S.top().squence.push_back(i);
                    break;
                }
                else{//严格小于的时候开始弹栈 
                    flag=true;
                    val=S.top();
                    S.pop(); 
                    if(!S.empty()){
                        L=S.top().v;
                    }
                    else L=-1;
                    len=val.squence.size();
                    for(int j=0;j<len;j++){
                        result[val.squence[j]].R=result[i].v;
                        result[val.squence[j]].L=L;
                    }
                }
            }
            if(flag){
                goto label;
            } 
        }
        else {//当入栈元素更小时直接压栈 
            label:store New;
            New.v=result[i].v;
            New.squence.push_back(i);
            S.push(New); 
        }
    }
    while(!S.empty()){
        val=S.top();
        S.pop();
        if(!S.empty()){
            L=S.top().v;
        }
        else L=-1;
        len=val.squence.size();
        for(int i=0;i<len;i++){
            result[val.squence[i]].R=R;
            result[val.squence[i]].L=L;
        }
    }
    for(int i=1;i<=n;i++){
        printf("第%d个:  值:%d   L:%d  R:%d\n",i,result[i].v,result[i].L,result[i].R);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39304630/article/details/81480330