单调栈结构

news2024/11/27 14:28:59

单调栈
单调栈是一种特殊设计的栈结构,为了解决如下的问题:
给定一个可能含有重复数值的 arr[],i位置的数一定存在如下两种信息:

  1. arr[i]的左侧离 i 最近并且小于(或者大于)arr[i] 的数在哪?
  2. arr[i]的右侧离 i 最近并且小于(或者大于)arr[i] 的数在哪?

如果想要得到arr[]中所有元素的这两个位置的信息,怎么能让得到信息的过程尽可能的快?

单调栈的实现

无重复值
拿左右侧距离arr[i]最近并且小于arr[i]来举例
假设当前有数组arr[] = {3,1,5,7,2,6,4},从0 ~ N-1顺序遍历。而我们的单调栈严格保持着栈底到栈顶是由小到大的顺序,此时栈中为null,所以0位置的3直接放入栈中。
在这里插入图片描述
下一个1位置1,如果此时将1放入栈中破坏了小 -> 大的栈结构,所以将0位置3弹出,弹出时收集0 - 3的左右侧距离最近的小于当前元素的信息。0 - 3弹出后栈中无元素,证明左侧没有比它小的,所以为 -1,右侧最近为1,此时弹出0->3 将 1-1放入栈中。
在这里插入图片描述
继续向下,数组下标2位置的5和3位置的7都可以保证栈由小到大的顺序,所以按照顺序放入栈中即可。此时数组下标来到了4位置的2。
在这里插入图片描述
4 - 2如果想要放入栈中,会破坏由小到大的顺序,所以此时弹出栈顶元素 3 - 7,3 - 7右侧最近并且小于当前元素的值为 4 - 2,左侧最近且小的元素为当前栈顶元素 2 - 5 ,3 - 7元素左右侧信息收集完成。
此时4 - 2依然不能放入栈中,因为当前栈顶元素为2 - 5,弹出栈顶元素2 - 5,收集 2 - 5 的信息,同样的,4 - 2元素过来导致的2 - 5元素出栈,所以 4 - 2 元素为2 - 5的右侧最近且小于当前元素的值。此时栈中剩余元素 1 - 1,所以2 - 5 右侧信息 4 - 2 左侧信息 1 -1。
栈中剩余元素 1 - 1,4 - 2放入满足条件,直接入栈。
在这里插入图片描述
下一个元素 5 - 6直接入栈,来到最后一个元素 6 - 4。
在这里插入图片描述
来看6 - 4,此时栈顶元素 5 - 6,直接入栈不符合有小到大要求,所以弹出栈顶元素 5 - 6,收集 5 - 6信息,6 - 4元素的到来导致的5 - 6元素的出栈,所以6 - 4为 5 - 6的右侧最近且小的元素,此时栈顶元素4 - 2为左侧最近且小的元素,收集好5 -6信息后,6 - 4元素入栈。
在这里插入图片描述
此时数组已经遍历完了,接下来怎么办?循环弹出栈中元素,把栈弹空即可。
因为此时已经不会再有新的元素加进来,所以栈中剩余元素的右侧最近且小的值都为 - 1。
弹出 6 - 4后,栈顶元素为 4 - 2,所以 4 - 2是6 - 4左侧最近且小的元素,右侧为-1。
弹出 4 - 2后,栈顶元素为1 - 1,所以 1 - 1是 4 - 2左侧最近且小的元素,右侧为 -1。
最后剩的元素1 - 1,弹出后栈为null,所以1 - 1左右两侧都没有最近且小的值, -1 -1。
数组中所有元素左右侧信息收集完毕。
在这里插入图片描述

证明(数组中无重复值)
我们来验证一下上面所说的流程,看它为什么是对的。
假设在某一步,B下面是A,此时由于C的到来B要出栈,此时要收集B的信息。

为什么C会是B右侧最近且小的数。

  1. C为什么会使B弹出? 由于我们单调栈的特性,所以一定是因为B的值大于C,并且是从数组0位置开始到 N - 1位置遍历,所以说数组中C一定在B的右边。
  2. B和C中间的数有没有可能小于B? 也不可能,如果BC中间有小于B的数,那B早就会被弹出栈,不会等C过来的时候才出栈。所以C才是B右边最近且小于B的数。

为什么B左边比它小的是A?

  1. 因为在栈中B在A的上面,所以在数组中B一定在A的右边。
  2. A和B中间的数,有没有可能小于A? 不可能,如果有这样的数,A早就会出栈,不会B下面压着A。
  3. A和B中间的数,有没有大于A 小于B? 也不可能,比如说A = 3 B = 7,在数组中是3 5 4 7这样的顺序。3进栈后5紧接着也会跟着进栈,但是到4的时候,5虽然出栈了,不过等7进栈时,B不会压着A,中间还会有个4,B能直接压着A说明AB中间没有 大于A小于B的数。
  4. A和B中间的数都是大于B的,B下面压着A,A在B的左边,那A不就是B左边最近且小的值么?
    在这里插入图片描述

数组中有重复值
如果数组中有重复值该怎么弄? 栈中存一个链表结构,相同值的数组下标放在一起,来看下面的图。
假设数组int[] arr = {1,3,4,5,4,3,1,2},刚来上0 - 1时,栈中为null,直接入栈,后面的3 4 5同样符合栈中元素从小到大的顺序,一次入栈。此时来到了数组下标位置为4的元素。
在这里插入图片描述
4 - 4小于当前栈顶元素 3 - 5,弹出栈顶元素,此时结算3 - 5元素的信息,因为是 4 - 4元素的到来导致我出栈的,所以我右侧最近且小的信息是4 - 4,左侧最近且小的元素为栈中下一个元素的链表中最后一个元素,在这里是2 - 4,所以3 - 5左侧最近且小的值2 - 4,右侧最近且小的值 4 - 4。还没完,此时栈顶元素2 - 4的值和4 - 4的值相等,统一放在链表中。
在这里插入图片描述
接下往下走,来到了5 - 3,此时栈顶元素大于当前值5 - 3,弹出栈顶元素,2 - 4和4 - 4两个值一起结算,因为是5 - 3的到来导致我们两个出栈,所以右侧最近且小的值为5- 3,左侧最近且小的值,依然是找栈中下一个元素的链表中的最后一个元素,在这里是1 - 3,一起结算的相同值的左右最近且最小元素一定是一样的,所以2 - 4和4 - 4左侧最近且小的值为1 - 3,右侧近且小的值为5 - 3。弹出后,5 - 3压入栈中。
在这里插入图片描述
在往下走,来到了6 - 1,同样处理,1 - 3 和 5 - 3 弹出,一起结算,左侧最近且小的值为 0 - 1,右侧最近且小的值为 6 - 1。6 - 1压入栈中。
在这里插入图片描述
最后7 - 2直接入栈。数组遍历到头了,循环遍历直接弹出栈中元素。依然是没有新的元素加入,所以栈中剩余元素所有的右侧最近且小的值为-1。
7 - 2左侧最近且小的值,看栈中下一个元素的链表中最后一个元素,在这里为 6 - 1,所以 7 - 2的左侧最近且小的值为 6 - 1,右侧最近且小为 -1。
剩余的 0 - 1元素和 6 - 1元素,左右最近且小的值都为-1。
在这里插入图片描述
数组中元素信息全部收集完成。

总结
那个元素使栈中元素弹出,即为弹出元素的右侧最近且小的值,弹出后的栈顶元素为弹出元素的左侧最近且小的值。数组长度为N,遍历完整个数组,所有元素再栈中最多进一次出一次,所以时间复杂度为 O ( N ) O(N) O(N)
如果数组中有重复值,则用链表维护相同值的元素下标。

代码
返回的二维数组中第一个数组代表arr中的下标,每个数组对应的第二个数组中一共两个值,0表示左侧最近且小,1表示右侧最近且小。
[
0 : [-1, 1]
1 : [-1, -1]
2 : [ 1, -1]
3 : [ 2, -1]
]
无重复值

 public static int[][] getNearLessNoRepeat(int[] arr) {
        int[][] result = new int[arr.length][2];

        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < arr.length; i++) {
            while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
                Integer cur = stack.pop();
                Integer leftLessIndex = stack.isEmpty() ? -1 : stack.peek();
                result[cur][0] = leftLessIndex;
                result[cur][1] = i;
            }
            stack.push(i);
        }
        while (!stack.isEmpty()) {
            Integer cur = stack.pop();
            Integer leftLessIndex = stack.isEmpty() ? -1 : stack.peek();
            result[cur][0] = leftLessIndex;
            result[cur][1] = -1;
        }
        return result;
    }

有重复值

 public static int[][] getNearLess(int[] arr) {
        int[][] res = new int[arr.length][2];
        Stack<List<Integer>> stack = new Stack<>();

        for (int i = 0; i < arr.length; i++) {
            while (!stack.isEmpty() && arr[stack.peek().get(0)] > arr[i]) {
                List<Integer> linked = stack.pop();
                Integer leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);

                for (Integer cur : linked) {
                    res[cur][0] = leftLessIndex;
                    res[cur][1] = i;
                }
            }
            if (!stack.isEmpty() && stack.peek().get(0) == i) {
                stack.peek().add(Integer.valueOf(i));
            } else {
                ArrayList<Integer> list = new ArrayList<>();
                list.add(i);
                stack.push(list);
            }
        }

        while (!stack.isEmpty()) {
            List<Integer> lists = stack.pop();
            Integer leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);

            for (Integer cur : lists) {
                res[cur][0] = leftLessIndex;
                res[cur][1] = -1;
            }
        }
        return res;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1300672.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

边缘AI行业报告:发展趋势、相关机遇、产业链及相关公司深度梳理

今天分享的AI系列深度研究报告&#xff1a;《边缘AI行业报告&#xff1a;发展趋势、相关机遇、产业链及相关公司深度梳理》。 &#xff08;报告出品方&#xff1a;慧 博 智 能 投 研&#xff09; 报告共计&#xff1a;31页 边缘计算及边缘 AI 概述 1.边缘AI 边缘 AI(本文指…

第四节JavaScript 条件语句、循环语句、break与continue语句

一、JavaScript条件语句 在通常的代码中&#xff0c;我们有一些需要决定执行不同动作&#xff0c;这就可以在代码中使用条件语句来完成。 下面是我们常使用的条件语句&#xff1a; if语句&#xff1a;只有当指定条件是true时&#xff0c;执行条件内代码。if…else语句&#…

代码序随想录二刷 |二叉树 | 二叉树的层序遍历II

代码序随想录二刷 &#xff5c;二叉树 &#xff5c; 二叉树的层序遍历II 题目描述解题思路代码实现 题目描述 107.二叉树的层序遍历II 给你二叉树的根节点 root &#xff0c;返回其节点值 自底向上的层序遍历 。 &#xff08;即按从叶子节点所在层到根节点所在的层&#xff0…

【Unity动画】Avatar Mask

创建 Avatar Mask可以设置那一部分骨骼运动和不运动 然后放在状态机里面的层中来混合 【后续完善】

JVM虚拟机系统性学习-运行时数据区(堆)

运行时数据区 JVM 由三部分组成&#xff1a;类加载系统、运行时数据区、执行引擎 下边讲一下运行时数据区中的构成 根据线程的使用情况分为两类&#xff1a; 线程独享&#xff08;此区域不需要垃圾回收&#xff09; 虚拟机栈、本地方法栈、程序计数器 线程共享&#xff08;数…

java接入gpt开发

前情提要 本次文章使用编译器为IDEA2020 使用GPT模型为百度旗下的千帆大模型 如果是个人用或者不流传出去&#xff0c;可以无脑入&#xff0c;因为会免费送20块钱&#xff08;够用上万次&#xff09; 代金卷查看 正式教程&#xff1a; 百度智能云控制台 (baidu.com) 按照步…

游戏提示找不到d3dx10_43.dll怎么办?5种方法教你如何修复

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“缺少d3dx10_43.dll文件”。这个错误提示通常出现在运行某些游戏或应用程序时&#xff0c;它意味着系统无法找到所需的动态链接库文件。本文将详细介绍d3dx10_43.dll文件的作用以及导致其丢…

《文化创新比较研究》期刊发表杂志投稿

《文化创新比较研究》是由国家新闻出版总署批准&#xff0c;黑龙江文化产业投资控股集团有限公司主管主办的学术期刊&#xff08;旬刊&#xff09;。 以学术研究为基础&#xff0c;始终坚持双百方针&#xff0c;立足理论前沿&#xff0c;关注学术热点&#xff0c;推动学术文化交…

kotlin - ViewBinding

前言 为什么用ViewBinding&#xff0c;而不用findViewById()&#xff0c;这个有很多优秀的博主都做了讲解&#xff0c;就不再列出了。 可参考下列博主的文章&#xff1a; kotlin ViewBinding的使用 文章里也给出了如何在gradle中做出相应的配置。 &#xff08;我建议先看这位博…

手把手教你玩转DHT11(原理+驱动)

大家生活中一定经常使用温湿度数据&#xff0c;比如&#xff1a;天气预报、智能家居、智慧大屏等等。这些数据可以通过温湿度传感器进行获取。在嵌入式开发中&#xff0c;温湿度传感器是一种十分常用的传感器。本文将为大家介绍温湿度传感器 DHT11&#xff0c;内容包含模块介绍…

Mac电脑投屏AirServer 2024怎么下载安装激活许可期限

对于那些想要将 iPhone、iPad 或其他 iOS 设备上的小屏幕镜像到计算机上的大屏幕的人来说&#xff0c;AirPlay 是一个很好的工具。 基于此&#xff0c;AirServer 非常需要将您的 Mac 或 PC 变成 AirPlay 设备。 但是如何使用计算机上的设置对 iPhone 等 iOS 设备进行屏幕镜像&a…

nginx中Include使用

1.include介绍 自己的理解&#xff1a;如果学过C语言的话&#xff0c;感觉和C语言中的Include引入是一样的&#xff0c;引入的文件中可以写任何东西&#xff0c;比如server相关信息&#xff0c;相当于替换的作用&#xff0c;一般情况下server是写在nginx.conf配置文件中的&…

三. LiDAR和Camera融合的BEV感知算法-BEVFusion实战

目录 前言1. BEVFusion实战 前言 自动驾驶之心推出的《国内首个BVE感知全栈系列学习教程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考 本次课程我们来学习下课程第三章——LiDAR和Camera融合的BEV感知算法&#xff0c;一起去学习下 BEVFusion 的相关代码 课…

理解MyBatis原理、思想

JDBC编码的缺点 结果集解析复杂&#xff0c;列名硬编码&#xff0c;sql变化导致解析代码变化&#xff0c;系统不易维护。Sql语句硬编码&#xff0c;难以维护。 数据库配置硬编码。频繁连接、释放数据库资源&#xff0c;降低系统性能。preparedStatement向占位符号传参数存在硬…

SpringBoot集成系列--xxlJob

文章目录 一、搭建调度中心xxl-job-admin1、下载项目2、调整项目参数3、执行初始化数据库SQL4、启动项目5、访问 二、集成步骤1、添加xxl-job的依赖2、添加xxl-job的依赖3、配置执行器4、创建执行器5、开发任务1&#xff09;方式1&#xff1a;BEAN模式&#xff08;方法形式&…

文心ERNIE Bot SDK+LangChain:基于文档、网页的个性化问答系统

现在各行各业纷纷选择接入大模型&#xff0c;其中最火且可行性最高的形式无异于智能文档问答助手&#xff0c;而LangChain是其中主流技术实现工具&#xff0c;能够轻松让大语言模型与外部数据相结合&#xff0c;从而构建智能问答系统。ERNIE Bot SDK已接入文心大模型4.0能力&am…

2023-12-10 LeetCode每日一题(爬楼梯)

2023-12-10每日一题 一、题目编号 70. 爬楼梯二、题目链接 点击跳转到题目位置 三、题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 示例 2&#xff1a; 提…

【vim】常用操作

用的时候看看&#xff0c;记太多也没用&#xff0c;下面都是最常用的&#xff0c;更多去查文档vim指令集。 以下均为正常模式下面操作&#xff0c;正在编辑的&#xff0c;先etc一下. 1/拷贝当前行 yy&#xff0c;5yy为拷贝包含当前行往下五行 2/p将拷贝的东西粘贴到当前行下…

(第一部分1-3 / 13)PMBOK 6

项目管理协会 PMI 价值观 责任、尊重、公正、诚实 法约尔 亨利法约尔_百度百科 管理的5项职能 计划组织指挥协调控制管理的14项原则&#xff08;该原则的提出&#xff0c;标志着法约尔管理理论的形成&#xff09; 【劳动分工】劳动分工是合理使用个人力量和集体力量的最好…

36 括号匹配问题

import java.util.HashMap; import java.util.LinkedList; import java.util.Scanner;public class Main {// 输入获取public static void main(String[] args) {Scanner sc new Scanner(System.in);String s sc.nextLine();System.out.println(getResult(s));}// 算法入口pu…