单调栈结构

本文介绍了如何使用单调栈解决一个数组中查找每个位置左右最近且值较小的元素问题,详细解析了单调栈的工作原理,并通过一个例子进行说明。文章通过分析单调栈的弹出规则,证明了其正确性,并鼓励读者运用类似思维解决其他算法问题。
摘要由CSDN通过智能技术生成

这篇博客是基于左程云的程序员代码面试指南的内容来写的

题目要求:

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

首先想到的肯定是暴力了,从每个数向二边开始去找。这样的复杂度为O(N^2),理解起来也很简单,代码如下展示:

        int l = -1;
        int r = -1;
        for (int i = 0; i < n; i++) {
            //先遍历左边
            for (int j = i; j >=0 ; j--) {
                if (res[j] < res[i]){
                    l = j;
                    break;
                }
            }
            for (int j = i; j < n; j++) {
                if (res[j] < res[i]){
                    r = j;
                    break;
                }
            }
            System.out.println(l + " " + r);
            l = -1;
            r = -1;
         }

就是从它的左右二边去标记下最小值就行,一旦找到就break。

下面介绍下单调栈,时间复杂度0(N),空间复杂度0(N);

单调栈的定义为:整个栈里的值都是一直变小或者一直变大的,不会出现违反这个规则的情况。

先对代码进行展示,在对它进行解释。

  Stack<Integer> result = new Stack<>();

        for (int i = 0; i < n; i++) {
            if (result.isEmpty()){//如果是空的话就把这个数放进去
                result.add(i);
            }else {
                if (res[i] < res[result.peek()]){//如果它小于等于
                    while (!result.isEmpty() && res[i] < res[result.peek()]){
                        Integer pop = result.pop();
                        if (result.isEmpty()){
                            temp[pop][0] = -1;
                        }else {
                            temp[pop][0] = result.peek();
                        }

                        temp[pop][1] = i;
                    }
                    result.add(i);
                }else {
                    result.add(i);
                }
            }
        }
        while (!result.isEmpty()){
            Integer pop = result.pop();
            if (result.isEmpty()){
                temp[pop][1] = -1;
                temp[pop][0] = -1;
            }else {
                temp[pop][1] = -1;
                temp[pop][0] = result.peek();
            }

        }
        for (int i = 0; i < n; i++) {
            System.out.println(temp[i][0] + " " + temp[i][1]);
        }

首先举个栗子:

对于一个数组: 4 2 6 8 1 3 7

1.首先拿出4,发展栈空,把4压入栈;

2.将2与栈顶4比较小于4,所以4的右边比它小的数就是2,左边比它小的是栈中它下面的数,如果不存在则为-1,4弹出后接着比较,发现栈空,2压入栈;

所以4的结果就是 -1 1;

3.6与栈顶元素比较,大于2直接压入栈;

4.8与6比较小于,直接压入栈;

5.1与8比较,小于8,所以8弹出,右边比它小的数就是1,左边比它小的是栈中它下面的数6,8弹出后接着进行比较。所以8的结果就是 2 4 ;

6.1与6比较,小于6,所以6弹出,右边比它小的数就是1,左边比它小的是栈中它下面的数2,6弹出后接着进行比较。所以6的结果就是 1 4 ;

7.1与2比较,小于2,所以6弹出,右边比它小的数就是1,左边比它小的是栈中它下面的数2,6弹出后接着进行比较。所以2的结果就是 -1 4;发现栈空,1压入栈;

8.3与栈顶元素比较,大于1直接压入栈;

9.7与栈顶元素比较,大于3直接压入栈;

10.所有数均压入栈完毕,此时如果栈不为空的话,则依次弹出,右边比它小的数为-1,左边为栈中下面的数组。

所以7的结果就是5 -1;

所以3的结果就是4 -1;

所以1的结果就是-1 -1;

大概流程,如图所示,emm第一次画不太会画。多包涵。

下面对为什么单调栈结构所弹出的数,一定是符合要求的进行解释

首先解释为什么放入的数字比栈顶数字小就一定是它右边最小的数字:

这里假设这个数字之前有一个数字比栈顶元素小,则这个栈顶元素一定早就弹出栈了,所以一定不会有这个数字到被弹出数字之间比被弹出数字小的数组。

其次解释为什么这个数字下面的数字就是比它小的最左边的第一个数字。

还假设下面的数字到这个数字之间还存在比它小的数字,那么根据上面的规则则这个数字肯定已经被弹出了。所以此时被弹出数字下面的数字一定是这个数字左边第一个比它小的数字。

至此解释完毕。其实这种假设的方法可以用来帮助解决很多这种算法结构的问题,hhh以后自己多体会吧。

单调栈的用法:后面有题目用到在来进行补充!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单调栈是一种特殊的栈结构,它的主要应用是用于解决求某个元素的左边或右边第一个比它大或小的元素的问题。单调栈的特点是栈中的元素保持有序,即栈顶元素最小或最大。而发射站是一个具体的应用场景,我们可以利用单调栈来解决相关问题。 在一个发射站中,我们需要找到每个位置的第一个比它大的位置。我们可以使用单调栈来实现这个功能。首先,我们将第一个元素的下标入栈。然后,依次遍历剩下的每个元素。对于每个元素,我们将它与栈顶元素进行比较。如果当前元素比栈顶元素大,说明找到了以栈顶元素为高度的发射站,我们可以更新它对应的输出结果,并弹出栈顶元素。直到当前元素小于或等于栈顶元素,我们将当前元素的下标入栈。这样,最后栈中剩下的元素对应的输出结果为-1,表示没有找到比它大的位置。 使用单调栈来解决发射站问题的时间复杂度为O(n),其中n为发射站的个数。这是因为每个元素最多入栈一次,出栈一次,所以遍历所有元素的时间复杂度为O(n)。而每个元素入栈和出栈的操作时间复杂度为O(1)。因此,总的时间复杂度为O(n)。 综上所述,利用单调栈结构可以高效地解决发射站问题。通过遍历发射站中的元素,可以找到每个位置的第一个比它大的位置。这种方法的时间复杂度为O(n),其中n为发射站的个数。因此,使用单调栈是一个有效的解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值