golang 代码实现单调栈

Posted 终点就在前方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang 代码实现单调栈相关的知识,希望对你有一定的参考价值。

有种特殊的栈,叫做单调栈,顾名思义就是栈底到栈顶呈现单调特性,比如对于列表:[]int6, 10, 3, 7, 4, 12,如果要实现单调栈,并不是所有入栈后呈降序或升序排列就是单调栈,如单调递增栈:

6 | 栈空,入栈 | [6]
10 | 10 > 6, 符合递增,入栈 | [6, 10]
3 | 3 < 10, 先全部出栈,把3入栈 | [3]
7 | 7 > 3, 符合递增,入栈 | [3, 7]
4 | 4 < 7, 7出栈,4入栈 | [3, 4]
12 | 12 > 4, 入栈 | [3, 4, 12]

所以最后的单调递增栈:[]int3, 4, 12

相应的 golang 实现:

// 单调递增栈,栈底: min, 栈顶: max
func leftMinRightMax(nums []int) []int 
	monotoneStack := []int
	res := make([]int, len(nums))

	for i, num := range nums 
		if len(monotoneStack) == 0 
			monotoneStack = append(monotoneStack, num)
			res[i] = -1
		 else 
			for len(monotoneStack) > 0 && num < monotoneStack[len(monotoneStack)-1] 
				monotoneStack = monotoneStack[:len(monotoneStack)-1]
				if len(monotoneStack) > 0 
					res[i] = monotoneStack[len(monotoneStack)-1]
				 else 
					res[i] = -1
				
			
			monotoneStack = append(monotoneStack, num)
		
	

	fmt.Println("res: ", res)
	return monotoneStack

根据单调递增栈的特性,给出单调递减栈的代码实现:

// 单调递减栈,栈底: max, 栈顶: min
func leftMaxRightMin(nums []int) []int 
	monotoneStack := []int
	res := make([]int, len(nums))

	for i, num := range nums 
		if len(monotoneStack) == 0 
			monotoneStack = append(monotoneStack, num)
			res[i] = -1
		 else 
			for len(monotoneStack) > 0 && num > monotoneStack[len(monotoneStack)-1] 
				monotoneStack = monotoneStack[:len(monotoneStack)-1]
			
			if len(monotoneStack) > 0 
				res[i] = monotoneStack[len(monotoneStack)-1]
			 else 
				res[i] = -1
			
			monotoneStack = append(monotoneStack, num)
		
	

	fmt.Println("res: ", res)
	return monotoneStack

浅谈单调栈的实现方式和简单应用

一、单调栈的原理和实现方式

1.定义

从栈底元素到栈顶元素呈单调递增或单调递减,栈内序列满足单调性的栈;

2.原理

(1)当新元素在单调性上优于栈顶时(单增栈新元素比栈顶大,单减栈新元素比栈顶小),压栈,栈深+1;
(2)当新元素在单调性与栈顶相同(新元素于栈顶相同)或劣于栈顶时(单增栈新元素比栈顶小,单减栈新元素比栈顶大),弹栈,栈深-1;

3.一般实现形式

以单增栈(栈顶为最大值)为例:
n为元素数,h为入栈序列,tot为栈深,stack为单增栈;

void stacks(){
        int stack[10001],tot=0,n,h;
        memset(stack,0,sizeof(stack));
        scanf("%d",&n);
        for(i=1;i<=n;++i){
                scanf("%d",&h);
                while(tot>0&&h<=stack[tot]) --tot;
                stack[++tot]=h;
        }
}

4.几点注意

(1)在弹栈过程中,注意增加限制条件tot>0,若无此条件,如果新元素为负数,弹栈过程中就可能出现tot<0的现象;
(2)在弹栈压栈过程中,注意随时更新栈深;
(3)多次使用时没必要每次都重置栈,有操作流程知数据直接覆盖不会出现问题,可以节省时间;

二、相关应用

下面我们通过由简到难介绍几个题目来说明单调栈的使用方式:

1.[模板]最长上升子序列 (LIS)

Description

给一个数组 a[1],a[2],...,a[n],找到最长的上升子序列a[b1]< a[b2]< ... < a[bk]的长度,其中b[1]<b[2]< ... <b[k]。

Solution

1.用单调栈优化,利用LIS的单调性,使用单增栈,栈顶为最大值;
2.对于每一个数:
(1)当其大于栈顶时,满足单调性,压栈;
(2)当其小于栈顶时,不满足单调性,向下搜索大于该数的最小值,替换;
(3)当其等于栈顶时,不满足单调性,不作处理;
3.栈深即为LIS的长度;

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring> 
#include<algorithm>
using namespace std;

int a[1005],b[1005],n;
int stk[1005];

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];  
    sort(b+1,b+n+1);
    int tot=unique(b+1,b+n+1)-(b+1);
    for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
    int top=0;
    for(int i=1;i<=n;i++){
        if(top==0||stk[top-1]<a[i]) stk[top++]=a[i];
        else{
            *upper_bound(stk,stk+top,a[i])=a[i];
        }
    }
    printf("%d\n",top);
    return 0;   
}

2.[POI 2008&洛谷P3467]PLA-Postering

Description

Byteburg市东边的建筑都是以旧结构形式建造的:建筑互相紧挨着,之间没有空间.它们共同形成了一条长长的,从东向西延伸的建筑物链(建筑物的高度不一).Byteburg市的市长Byteasar,决定将这个建筑物链的一侧用海报覆盖住.并且想用最少的海报数量,海报是矩形的.海报与海报之间不能重叠,但是可以相互挨着(即它们具有公共边),每一个海报都必须贴近墙并且建筑物链的整个一侧必须被覆盖(意思是:海报需要将一侧全部覆盖,并且不能超出建筑物链)
输入格式:第一行为一个整数n(1≤n≤250000),表示有n个建筑,接下来n行中,第i行表示第i个建筑物的宽di与高wi(1≤di,wi≤1 000 000 000),中间由一个空格隔开;
输出格式:第一个为一个整数,表示最少需要几张海报覆盖整个建筑物.

Solution

1.考虑如果整个建筑物链是等高的,一张高为链高,宽为整个建筑物宽的海报即可完全覆盖;
2.若有两个不等高的元素组成建筑物链,那么一定需要两张;
3.因为题目要求海报不可超出建筑物链,那么我们即可用单调栈维护:初始海报数为建筑物数,入栈建筑物链的高度序列,当栈顶大于即将入栈元素时弹栈,若最后弹栈元素与即将入栈元素等高,需要的海报数-1;
4.易证明本方法是正确的:当有两个处于一个峰两侧的等高块时,他们可以用一张海报覆盖,所需海报数显然可减少一个;

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm> 
using namespace std;
long long top=0,n,num=0,i,j,k,stack[250100];
int main(){
    scanf("%lld",&n);
    for(i=1;i<=n;++i){
        scanf("%lld%lld",&j,&k);
        while(top>0&&k<=stack[top]){
            if(k==stack[top])num++;
            --top;
        }
        stack[++top]=k;
    }
    printf("%lld\n",n-num);
    return 0;
}

以上是关于golang 代码实现单调栈的主要内容,如果未能解决你的问题,请参考以下文章

模拟初级计算器 单调栈实现

POJ 2559 单调栈

算法学习——剑指 Offer II 039. 直方图最大矩形面积(Java实现蛮力,分治,单调栈)

算法学习——剑指 Offer II 039. 直方图最大矩形面积(Java实现蛮力,分治,单调栈)

算法学习——剑指 Offer II 039. 直方图最大矩形面积(Java实现蛮力,分治,单调栈)

单调队列与单调栈作用