详细分析单调栈,及正确性证明

news2025/1/13 3:06:55

什么是单调栈

对于一个数组,需要对每个位置生成,左右两边离它最近的,比它小(或比它大)的位置在哪

例如:
在这里插入图片描述

如果对每个位置都遍历下左右两边,找到第一个比它小的位置,就是O(N ^ 2)的算法

单调栈结构就是专门解决这种问题,能做到整个过程的时间复杂度为O(N)

流程

准备一个栈,栈中保存元素的下标,要求元素从从栈底到栈顶由小到大的顺序排列

从左到右遍历整个数组arr

当遍历到一个数i时,如果arr[i]比栈顶的元素peek所代表的数arr[peek]小,此时如果将arr[i]压入栈中,就改变了栈的顺序,使得从栈底到栈顶不是由小到大,因此需要将栈顶元素peek弹出

此时位置为peek的数:

  • 在它左边离它最近且比它小的数,就是现在的栈顶元素代表的数

    • 如果此时栈是空的,说明它左边没有比它小的数
  • 在它右边离它最近且比它小的数,就是arr[i]

当遍历完整个数组后,开始清理栈中的元素,依次从栈顶弹出元素:

  • 在它左边离它最近且比它小的数,就是现在的栈顶元素代表的数

    • 如果此时栈是空的,说明它左边没有比它小的数
  • 在它右边离它最近且比它小的数:没有

这个流程为什么正确?下文有详细的正确性证明

代码如下:

public  int[][] getNearestLess(int[] arr) {
    int n = arr.length;
    // 返回每个位置i,左边最近最小的位置:res[i][0],右边最近最小的位置:res[i][1]
    int[][] res = new  int[n][2];
    Stack<Integer> stack = new Stack<>();
    for (int i = 0;i<n;i++) {
        while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
            Integer pop = stack.pop();
            // 收集弹出的数的答案
            res[pop][0] = stack.isEmpty() ? -1 : stack.peek();
            res[pop][1] = i;
        }

        stack.push(i);
    }

    // 处理栈中剩下的数
    while (!stack.isEmpty()) {
        Integer pop = stack.pop();
        res[pop][0] = stack.isEmpty() ? -1 : stack.peek();
        res[pop][1] = -1;
    }

    return res;
}

时间复杂度

由于在每次循环中的操作,就是让一些数进栈,出栈,每个位置最多进栈一次,出栈一次,不可能大于一次

因此整个流程耗时O(N)

正确性证明

当从栈中弹出一个元素时,为什么让它弹出的这个数,就是它右边离它最近,且比它小的数?

假设此时栈中栈顶元素为b,b下面压着a,遍历到c,且b < c

在这里插入图片描述

因为遍历到c时,b在栈中,因此b的下标比c的下标小,b在c的左边

b和c中间,有没有可能存在比b更小的数?

在这里插入图片描述

答案是不可能,因为如果存在,假设为k,那在遍历到k时,就会把b弹出,而轮不到现在c来弹出b

因此,b和c中间没有比b小的数,而现在c < b因此c就是b右边离b最近,且比它小的数

再来证明,为什么b左边离b最近,且比它小的数为a

因为b压着a,因此在数值中,a一定在b的左边

讨论a和b之间的数,有哪些可能性
在这里插入图片描述

  • 小于a:不可能,因为如果有,在遍历到这个数时就把a弹出了,而现在栈中还有a

  • 大于a,小于b:不可能:

    • 因为如果有这种数,这个数现在会压在a的上面,而不是b来压在a的上面。
    • 当然,这个数可能在遍历到b时,就被弹出了,但使得该数弹出的数,也会压在a的上面,而不是现在b压在a的上面
    • 无论怎样,只要a和b之间有大于a小于b的数,都不会轮到b来压在a的上面

综上所述,a和b之间只会有大于等于b的数,因此a就是b左边离b最近,且比它小的数

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

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

相关文章

IPv6 时代如何防御 DDoS 攻击?

在互联网世界&#xff0c;每台联网的设备都被分配了一个用于标识和位置定义的 IP 地址。20 世纪 90 年代以来互联网的快速发展&#xff0c;联网设备所需的地址远远多于可用 IPv4 地址的数量&#xff0c;导致了 IPv4 地址耗尽。因此&#xff0c;协议 IPv6 的开发和部署已经刻不容…

从第三方平台导出大数据量本地Excel怎么解决性能问题?

对于日常需要做分析的我们来说&#xff0c;周期性需要从第三方系统导出数据&#xff0c;日积月累数据量越来愈大&#xff0c;由开始的几百条数据慢慢增至十几万甚至百万级的数据量&#xff0c;在本地Excel直接做分析汇总老是卡顿等半天&#xff0c;效率日益低下&#xff0c;每天…

连续四年发布科技趋势预测,他们在探索中国科技的“主干道”

&#xff0c;*本文配图由百度飞桨文心一格提供AI绘画技术支持。古希腊流传着一句谚语&#xff1a;智慧不仅是能够明察眼前&#xff0c;更要能够预测未来。身处科技界&#xff0c;一到年底年初我们就会看到各种各样的趋势预测。这些预测五花八门&#xff0c;神奇多变。但大多数科…

JAVA中使用最广泛的本地缓存?Ehcache的自信从何而来3 —— 本地缓存变身分布式集群缓存,打破本地缓存天花板

大家好&#xff0c;又见面了。 本文是笔者作为掘金技术社区签约作者的身份输出的缓存专栏系列内容&#xff0c;将会通过系列专题&#xff0c;讲清楚缓存的方方面面。如果感兴趣&#xff0c;欢迎关注以获取后续更新。 上一篇文章中&#xff0c;我们知晓了如何在项目中通过不同的…

【Python】Numpy处理多项式类Polynomial

文章目录构造函数求导和积分求根和反演采样与拟合其他方法构造函数 Numpy中提供了多项式模块&#xff0c;里面封装了一些用以快速解决多项式问题的类和函数&#xff0c;其中最重要类的自然是Polynomial&#xff0c;其构造函数为 class numpy.polynomial.polynomial.Polynomia…

list容器的底层结构(详述insert()与erase())

目录 一、带头结点的双向循环链表&#xff08;list&#xff09; 二、贯穿list容器的insert与erase接口​编辑 一、带头结点的双向循环链表&#xff08;list&#xff09; 二、贯穿list容器的insert与erase接口 通过在指定位置的元素之前插入新元素来扩展容器。 这有效地增加了…

520页(17万字)集团大数据平台整体解决方案-v1.0

【版权声明】本资料来源网络&#xff0c;知识分享&#xff0c;仅供个人学习&#xff0c;请勿商用。【侵删致歉】如有侵权请联系小编&#xff0c;将在收到信息后第一时间删除&#xff01;完整资料领取见文末&#xff0c;部分资料内容&#xff1a; 1.1.1 系统总体逻辑结构 4-14系…

Golang 面试题总结

一.基础部分 go语言的值类型和引用类型&#xff1f; 值类型&#xff1a;int、float、bool、string和数组这些类型都属于值类型。 值类型的变量直接指向存在内存中的值&#xff0c;值类型的变量的值存储在栈中。当使用 将一个变量的值赋给另一个变量时&#xff0c;如 j i ,实…

九、k8s 安全认证

文章目录1 访问控制概述2 认证管理3 授权管理4 准入控制1 访问控制概述 Kubernetes作为一个分布式集群的管理工具&#xff0c;保证集群的安全性是其一个重要的任务。所谓的安全性其实就是保证对Kubernetes的各种客户端进行认证和鉴权操作。 客户端 在Kubernetes集群中&#…

MySQL调优-深入理解MVCC机制

目录 MySQL调优-深入理解MVCC机制 MVCC多版本并发控制机制 undo日志版本链与read view机制详解 根据图2和图3对应画出下图的undo日志版本链&#xff1a; 版本链比对规则&#xff1a; 注意&#xff1a; 举例1&#xff1a;分析一下下图select1的read_view以及各个select语句…

Cadence PCB仿真使用Allegro PCB SI导入其他板卡的层叠结构的方法图文教程

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 目录 1,概述2,导入方法3,总结1,概述 本文详细介绍使用Allegro PCB SI PCB仿真软件导入其他电路板层叠结构的方法。 2,导入方法 第1步:打开待仿真的PCB文件,并确认软件为Allegro PCB SI 如果,打开软件不是Allegro PC…

STL-vector的接口使用及模拟实现

文章目录vector类的介绍vector类的常用接口介绍 构造相关 无参构造迭代器区间构造拷贝构造 容量相关的接口 sizereserveresizecapacityempty 数据访问及遍历相关的接口 operator[]begin endrbegin rend 修改数据相关的接口 push_backpop_backinserterase vector类的模拟实现…

excel统计函数:应用广泛的动态统计之王OFFSET 下篇

【前言】在上篇文章中&#xff0c;我们了解了OFFSET函数的运算原理和各个参数的作用&#xff0c;并且我们也通过一些OFFSET的案例&#xff0c;了解了它的用途。那么本篇我们继续来看看&#xff0c;OFFSET函数在实际工作中所能起到的强大效果吧。一、高阶应用的思路&#xff08;…

vector使用指南

目录 引言 空间配置器 vector 与 string的一些差异 vector容器与string容器的一些差异 接口介绍——reserve resize接口 shrink_to_fit 接口 operator[ ] 和 at 接口 assign接口 增删查改接口 swap接口 例题讲解 引言 vector实质上就是数据结构的顺序表&#xff0…

数据结构:栈和队列(详细讲解)

&#x1f387;&#x1f387;&#x1f387;作者&#xff1a; 小鱼不会骑车 &#x1f386;&#x1f386;&#x1f386;专栏&#xff1a; 《数据结构》 &#x1f393;&#x1f393;&#x1f393;个人简介&#xff1a; 一名专科大一在读的小比特&#xff0c;努力学习编程是我唯一…

(8)Qt中的自定义信号

目录 自定义信号需要遵循的规则 信号的发送 自定义信号的基本实现 使用一个父子窗口切换的小案例 Qt框架提供的信号在某些特定场景下是无法满足我们的项目需求的&#xff0c;因此我们还设计自己需要的的信号&#xff0c;同样还是使用connect()对自定义的信号槽进行连接。 自…

制造业ERP管理系统解决方案之销售管理

在企业的生存发展中&#xff0c;销售管理起到了重要的作用&#xff0c;它决定着企业发展的提速和效益的提升。做好销售管理工作&#xff0c;不仅可以推动企业资金有效运转&#xff0c;还可以使企业在稳定中高效发展&#xff0c;使企业价值最大化。而在制造企业销售管理中&#…

Leetcode.1658 将 x 减到 0 的最小操作数

题目链接 Leetcode.1658 将 x 减到 0 的最小操作数 题目描述 给你一个整数数组 nums 和一个整数 x 。每一次操作时&#xff0c;你应当移除数组 nums 最左边或最右边的元素&#xff0c;然后从 x 中减去该元素的值。请注意&#xff0c;需要 修改 数组以供接下来的操作使用。 如…

SHELL脚本学习 --- 第八次作业(安全脚本)

SHELL脚本学习 — 第八次作业 题目要求&#xff1a; 将密码输入错误超过4次的IP地址通过firewalld防火墙阻止访问 思路&#xff1a; 首先需要找到ssh密码输入错误超过四次的IP地址&#xff0c;需要到日志文件中找。 该日志文件为/var/log/secure。 找到之后通过正则匹配到密码输…

JavaEE高阶---SpringBoot 统⼀功能处理

一&#xff1a;什么是SpringBoot 统⼀功能处理 SpringBoot统一功能处理是AOP的实战环节。我们主要学习三方面内容&#xff1a; 统一用户登录权限验证&#xff1b;统一数据格式返回&#xff1b;统一异常处理。 二&#xff1a;统一用户登录权限验证 Spring 中提供了具体的实现…