单调栈结构

单调栈
单调栈是一种特殊设计的栈结构,为了解决如下的问题:
给定一个可能含有重复数值的 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;
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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

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

二叉搜索树中第K小的元素[中等]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给定一个二叉搜索树的根节点root&#xff0c;和一个整数k&#xff0c;请你设计一个算法查找其中第k个最小元素&#xff08;从1开始计数&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,1,4,null,2], k 1 输出&#x…

编译Android14 AOSP原生代码并运行X86模拟器镜像过程记录

最近在研究Android Entreprise部分的特性&#xff0c;需要在Android手机上分析WorkProfile相关的源码&#xff0c;因为新买的Pixel样机还未到货&#xff0c;看了几天Android源码&#xff0c;迫切需要上真机对比分析。 又听说最近几年Android模拟器已经有些进步&#xff0c;至少…

【利用二手车数据进行可视化分析】

利用二手车数据进行可视化分析 查看原始数据去除重复数据需求分析1.统计全国总共有多少量二手车&#xff0c;用KPI图进行展示2.统计安徽总共有多少量二手车&#xff0c;用KPI图进行展示3.统计合肥总共有多少量二手车&#xff0c;用KPI图进行展示4.取最贵的10辆二手车信息&#…

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

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

class066 一维动态规划【算法】

class066 一维动态规划 算法讲解066【必备】从递归入手一维动态规划 code1 509斐波那契数列 // 斐波那契数 // 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 // 该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。…

从关键新闻和最新技术看AI行业发展(2023.11.20-12.3第十一期) |【WeThinkIn老实人报】

Rocky Ding 公众号&#xff1a;WeThinkIn 写在前面 【WeThinkIn老实人报】旨在整理&挖掘AI行业的关键新闻和最新技术&#xff0c;同时Rocky会对这些关键信息进行解读&#xff0c;力求让读者们能从容跟随AI科技潮流。也欢迎大家提出宝贵的优化建议&#xff0c;一起交流学习&…

睡觉时总在凌晨3、4点醒来,再无睡意,一般暗示四大疾病,别忽视!

良好的睡眠是人体健康的重要因素&#xff0c;正常情况下的八小时睡眠时间在人们一天的时间中占比三分之一&#xff0c;睡眠是人体器官进行自我修复和调整的关键时间&#xff0c;睡眠质量不好&#xff0c;则会影响各个器官的健康&#xff0c;人们可以从睡眠中发现身体的异常&…

常见web漏洞的流量分析

常见web漏洞的流量分析 文章目录 常见web漏洞的流量分析工具sql注入的流量分析XSS注入的流量分析文件上传漏洞流量分析文件包含漏洞流量分析文件读取漏洞流量分析ssrf流量分析shiro反序列化流量分析jwt流量分析暴力破解流量分析命令执行流量分析反弹shell 工具 攻击机受害机wi…

midwayjs从零开始创建项目,连接mikro-orm框架(必须有java的springboot基础)

前言&#xff1a; 我一直都是用java的springboot开发项目&#xff0c;然后进来新公司之后&#xff0c;公司的后端是用node.js&#xff0c;然后框架用的是 midwayjs &#xff0c;然后网上的资料比较少&#xff0c;在此特地记录一波 文档&#xff1a; 1.官方文档&#xff1a;介绍…

Linux C/C++ 从内存转储中恢复64位ELF可执行文件

ELF&#xff08;Executable and Linking Format&#xff09;是一种对象文件的格式&#xff0c;它主要用于定义ELF&#xff08;Executable and Linking Format&#xff09;是一种对象文件的格式&#xff0c;它主要用于定义不同类型的对象文件中的内容以及它们的存储方式。一个EL…

深入了解对象与内置构造函数

1. 深入对象 1.1 创建对象的三种方式 1.2 构造函数 语法约定&#xff1a; 总结 构造函数可以快速创建多个对象大写字母开头的函数使用new关键字将对象实例化构造函数不需要返回值自动返回新的对象 new实例化的执行过程 创建空对象this指向对象执行代码&#xff0c;追加新…

class069 从递归入手三维动态规划【算法】

class069 从递归入手三维动态规划 code1 474. 一和零 // 一和零(多维费用背包) // 给你一个二进制字符串数组 strs 和两个整数 m 和 n // 请你找出并返回 strs 的最大子集的长度 // 该子集中 最多 有 m 个 0 和 n 个 1 // 如果 x 的所有元素也是 y 的元素&#xff0c;集合 x 是…

SVN修改已提交版本的日志方法

1.在工做中一直是使用svn进行項目的版本控制的&#xff0c;有时候因为提交匆忙&#xff0c;或是忘了添加Log&#xff0c;或是Log内容有错误。遇到此类状况&#xff0c;想要在查看项目的日志时添加log或是修改log内容&#xff0c;遇到以下错误&#xff1a; Repository has not b…

2023最新最全【Wireshark 】 安装教程(附安装包)

简介 wireshark是非常流行的网络封包分析工具&#xff0c;功能十分强大。可以截取各种网络封包&#xff0c;显示网络封包的详细信息。使用wireshark的人必须了解网络协议&#xff0c;否则就看不懂wireshark了。 为了安全考虑&#xff0c;wireshark只能查看封包&#xff0c;而…

公式识别任务各个链条全部打通

目录 引言公式识别任务是什么&#xff1f;公式识别任务解决方案初探使用建议写在最后 引言 随着LaTeX-OCR模型转换问题的解决&#xff0c;公式识别任务中各个链条已经全部打通。小伙伴们可以放开膀子干了。 解决业界问题的方案&#xff0c;并不是单独训练一个模型就完事了&am…

汽车网络安全--关于UN R155认证的思考

1.UN R155概述 2020年6月25日,联合国颁布了全球首个汽车网络安全强制性法规 -- UN 155,详细规定了关于评估网络安全措施的审核条款、制造商和供应商降低网络安全风险的方法以及实施风险评估的义务等。 法规适用于与信息安全相关的M类(4轮及以上载客汽车)、N类(四轮载货汽车)…

[MySQL--进阶篇]存储引擎的体系结构、简介、特点、选择

前言 ⭐Hello!这里是欧_aita的博客。 ⭐今日语录&#xff1a;不要在乎别人怎么看你&#xff0c;因为他们根本就没有时间&#xff0c;他们只关心他们自己。 ⭐个人主页&#xff1a;欧_aita ψ(._. )>⭐个人专栏&#xff1a; 数据结构与算法 MySQL数据库 存储引擎 前言MySQL体…

Leetcode刷题笔记题解(C++):25. K 个一组翻转链表

思路&#xff1a;利用栈的特性&#xff0c;K个节点压入栈中依次弹出组成新的链表&#xff0c;不够K个节点则保持不变 /*** struct ListNode {* int val;* struct ListNode *next;* ListNode(int x) : val(x), next(nullptr) {}* };*/ #include <stack> class Solution { …

【数据结构 — 排序 — 选择排序】

数据结构 — 排序 — 选择排序 一.选择排序1.基本思想2.直接选择排序2.1算法讲解2.2.代码实现2.2.1.函数定义2.2.2.算法接口实现2.2.3.测试代码实现2.2.4.测试展示 3.堆排序3.1.算法讲解3.2.代码实现3.2.1.函数定义3.2.2.算法接口实现3.2.3.测试代码实现3.2.4.测试展示 一.选择…