单调栈的讲解及例题

单调栈顾名思义,就是栈中元素呈现单调性。

例题一:单调栈

给定一个长度为N的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出-1。

输入格式
第一行包含整数N,表示数列长度。

第二行包含N个整数,表示整数数列。

输出格式
共一行,包含N个整数,其中第i个数表示第i个数的左边第一个比它小的数,如果不存在则输出-1。

数据范围
1≤N≤105
1≤数列中元素≤109
输入样例:
5
3 4 2 7 5
输出样例:
-1 3 -1 2 2

解题思路及伪代码

for(int i = 0; i <n; i++ ){
	if(栈为空并且栈顶元素大于入栈元素){
		栈顶元素出栈;
		更新结果;
		}
	将当前元素可以拓展到的位置入栈
	更新最后一次出栈的栈顶元素其对应的值;
	}

单调栈做法

java代码实现:

import java.util.*;
class Main{
    public static void main(String []args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr = new int[n];
        int idx = 0;
        for(int i = 0; i < n; i++){
            int x = sc.nextInt();
            while(idx!=0&&arr[idx]>=x){
                idx--;
            }
            if(idx!=0){
                System.out.print(arr[idx]+" ");
            }else{
                System.out.print("-1"+" ");
            }
            arr[++idx] = x;
        }
    }
}

例题二:直方图中最大的矩形

直方图是由在公共基线处对齐的一系列矩形组成的多边形。

矩形具有相等的宽度,但可以具有不同的高度。

例如,图例左侧显示了由高度为2,1,4,5,1,3,3的矩形组成的直方图,矩形的宽度都为1:
在这里插入图片描述

通常,直方图用于表示离散分布,例如,文本中字符的频率。

现在,请你计算在公共基线处对齐的直方图中最大矩形的面积。

图例右图显示了所描绘直方图的最大对齐矩形。

输入格式
输入包含几个测试用例。

每个测试用例占据一行,用以描述一个直方图,并以整数n开始,表示组成直方图的矩形数目。

然后跟随n个整数h1,…,hn。

这些数字以从左到右的顺序表示直方图的各个矩形的高度。

每个矩形的宽度为1。

同行数字用空格隔开。

当输入用例为n=0时,结束输入,且该用例不用考虑。

输出格式
对于每一个测试用例,输出一个整数,代表指定直方图中最大矩形的区域面积。

每个数据占一行。

请注意,此矩形必须在公共基线处对齐。

数据范围
1≤n≤100000,
0≤hi≤1000000000
输入样例:
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
输出样例:
8
4000

解题思路

在这里插入图片描述

暴力做法

先读入数组中的值
int max = 0;
for(int i = 0; i < n; i ++){
	for(int j = i;j < n; j++){
		if(arr[j]>=arr[i]){
			len++;
		}else{
			break;
		}
	}
	max = Math.max(len*arr[i],max);
}

这道题其实就是对于每个数找到左边第一个比它小的数的位置,然后找到右边第一个比它小的位置,(右边-左边-1)*高度然后做一下max的比较即可。
单调栈就是一个里面的数单调递增的数的栈,一旦栈顶的元素大于或者等于要加入的数的话,这个数就出栈。

单调栈做法

import java.util.*;
class Main{
    static int N = 100010;
    //存储高度
    static int[] h = new int[N];
    //存储左边小于第i个数的第一个数
    static int[] l = new int[N];
    //存储右边大于第i个数的第一个数
    static int[] r = new int[N];
    //临时数组用来存储位置就是下标
    static int[] q = new int[N];
    public static void main(String [] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        while(n != 0){
            for(int i = 1; i <= n; i++){
                h[i] = sc.nextInt();
            }
            //方便特判,高度最小都是0,不存在比0小的数所以开-1
            h[0] = h[n+1] = -1;
            //求左边界
            int tt = 0;
            q[0] = 0;
            for(int i = 1; i <= n; i++){
            //如果前面的数大于等于当前数的话就出栈,就头指针--
                while(h[q[tt]]>=h[i]){
                    tt--;
                }
                //否则就存储说明当前的值就可以存储到l[i]中
                l[i] = q[tt];
                //然后将当前下标加到里面继续组成单调递增的序列
                q[++tt] = i;
            }
            //求右边界
            tt = 0;
            q[0] = n+1;
            for(int i = n; i > 0;i--){
                while(h[q[tt]] >= h[i]){
                    tt--;
                }
                r[i] = q[tt];
                q[++tt]  = i;
            }
            //数据过大,记得开long类型
            long res = 0;
            for(int i = 1; i <= n; i++){
                res = Math.max(res,(long)h[i]*(r[i]-l[i]-1));
            }
            System.out.println(res);
            n = sc.nextInt();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值