数据结构-单调栈2

news2024/12/25 0:20:22

这里是解了一个新的题目,然后对于有重复值的单调栈做了一些改进(只适用于特殊题目):有重复值的单调栈不再使用ArrayList或者LinkedList,而是像无重复值的那样直接使用下标,这种方法能保证最终的正确性,具体就是对于最后一个元素的左边第一个比他小的元素和右边第一个比他小的元素的计算是正确的,但是对于中间的重复值,右边第一个比他小的元素的计算可能是不正确的。

题目如下:

给定一个只包含正数的数组arrarr中任何一个子数组sub

一定都可以算出(sub累加和 )* (sub中的最小值)是什么,

那么所有子数组中,这个值最大是多少?

解题思路:尝试所有位置,找以当前数为最小值的范围的(sub累加和 )* 当前数的值,所有位置都会尝试,结果必在其中。

具体的看代码:

package dataStructure.danDiaoZhan.practice;


import java.util.Stack;

/**
 * 给定一个只包含正数的数组arr,arr中任何一个子数组sub,
 * 一定都可以算出(sub累加和 )* (sub中的最小值)是什么,
 * 那么所有子数组中,这个值最大是多少?
 */
public class AllTimesMinToMax {
    public static int process(int[] nums) {
         if(nums == null || nums.length == 0) {
             return -1;
         }
         //先设置为0,因为是正数数组,所以后面求的值都会比这个大
         int allTimesMin = 0;
         //算一个前缀和数组
         int[] preSum = new int[nums.length];
         for(int i = 0; i < nums.length; i++) {
             preSum[i] = i == 0? nums[i] : nums[i] + preSum[i - 1];
         }
         //对于每个位置,计算它的左边第一个比它小的元素和右边第一个比它小的元素
         int[][] nearLessArr = getNearLess(nums);
         for(int i = 0; i < nums.length; i++) {
             //nearLess是表示左边第一个比他小和右边第一个比它小的数组
             int[] nearLess = nearLessArr[i];
             //int leftBound = nearLess[0] == -1? 0 : nearLess[0];
             //找当前以nums[i]为最小元素的右边界,如果右边第一个比它小的数是-1
             //右边界就持续到数组最后一个元素,如果不是就持续到nearLess[1] - 1(nearLess[1]是第一个比他小的,那nearLess[1] - 1就是最后一个大于等于它的)
             int rightBound = nearLess[1] == -1? nums.length - 1 : nearLess[1] - 1;
             //System.out.println("nearLess[0]="+nearLess[0]);
             //这里一定要注意括号
             //(nearLess[0] == -1? 0 : preSum[nearLess[0]])是找当前范围前一个的累加和
             //如果nearLess[0] == -1表示左边没有比它小的,也就是以它为最小元素可以扩到数组开头
             //否则扩到nearLess[0]+1
             int preSumCurRange = preSum[rightBound] - (nearLess[0] == -1? 0 : preSum[nearLess[0]]);
             //每次比较计算最大值
             allTimesMin = Math.max(allTimesMin, nums[i] * preSumCurRange);
         }
         return allTimesMin;
    }

    /**
     * 改进后的单调栈,这里是可以兼容有重复值的情况,Stack里的数据不再是ArrayList或者LinkedList
     * 而是使用Integer类型,表示原来在数组中的下标
     * 这种的话对于重复值的情况,如果不是重复值的最后一个,那弹出时算的结果应该是不对的
     * (被自己的重复值元素弹出时右边第一个比他小的不对,因为是被重复值弹出的,实际上还没有找到右边第一个比他小的数),
     * 但是对于我们这个题来说是不影响结果的
     * 因为重复值的最后一个弹出的时候(包括最后独立清算的时候弹出)算的结果肯定是对的
     * @param nums
     * @return
     */
    public static int[][] getNearLess(int[] nums) {
        int[][] nearLessArr = new int[nums.length][2];
        //单调栈,依然是自底到顶从小到大,里面放的是数组中的下标
        Stack<Integer> stack = new Stack<>();
        for(int i = 0; i < nums.length; i++) {
            //如果栈不是空的且栈顶元素大于等于当前的元素,把栈顶依次弹出
            while(!stack.isEmpty() && nums[stack.peek()] >= nums[i]) {
                int popNumIndex = stack.pop();
                //左边第一个比他小的元素的计算是正确的
                nearLessArr[popNumIndex][0] = stack.isEmpty()? -1 : stack.peek();
                //对于右边第一个比他小的数,如果是被重复值弹出的这里计算的是重复值,但是这种是不对的
                // 因为重复值并不是右边第一个比他小的(他们相等),但是最后一个重复值肯定是正确的
                nearLessArr[popNumIndex][1] = i;
            }
            //当前元素下标压入栈
            stack.push(i);
        }
        //最后的独立结算过程
        while(!stack.isEmpty()) {
            int popNumIndex = stack.pop();
            nearLessArr[popNumIndex][0] = stack.isEmpty()? -1 : stack.peek();
            nearLessArr[popNumIndex][1] = -1;
        }
        return nearLessArr;
    }

    public static void main(String[] args) {
        //int[] nums = {5,5,5};
        int[] nums = {1,2,2,2,2,2,1};
        int allTimeMinMax = process(nums);
        System.out.println(allTimeMinMax);
    }
}

代码我觉得已经注释到详细的不能再详细了,如果还是不懂,看一下我下面的过程分析图:

一般场景的分析

结果是对的。 

极端场景

可以看到还是正确的 

还是不懂的话可以私信我

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

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

相关文章

Python图片转字符画,太好玩啦(46)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 和猫妹学Python&#xff0c;一起趣味学编程。 今日主题 什么是字符画呢&#xff1f; 如何用Python将图片转为字符画&#xff1f; 我们先找一幅原画&#xff0c;比如它吧&…

亚马逊云科技 一周回顾 – 2022 年 7 月 18 日

上周&#xff0c;Amazon 峰会&#xff08;纽约&#xff09;在贾维茨中心线下举办&#xff0c;有数千名与会者以及 100 多家赞助商和合作伙伴参加。在主题演讲中&#xff0c;Amazon 首席开发人员倡导者 Martin Beeby 谈到了云基础设施的创新如何帮助客户适应挑战并抓住新的机遇。…

知识积累(1)

&#xff08;1&#xff09; 当您在Git中看到消息 "HEAD is now at 1343ccb FAB-17419 Fix off_chain_data sample error (#146)" 时&#xff0c;这是Git告知您当前所在的分支和最新的提交哈希。 这条消息通常出现在使用Git命令后&#xff0c;如git pull或git check…

【Java校招面试】实战面经(四)

目录 前言一、Http协议状态码301和302的区别二、Time Wait状态的作用是什么&#xff1f;三、ConcurrentHashMap在JDK1.7和JDK1.8的区别四、MySQL的优化&#xff1a;怎么优化SQL、用过MySQL的性能分析工具吗&#xff1f;五、反转数组的算法六、JDBC怎么使用的&#xff0c;什么是…

Redis三种模式——主从复制、哨兵模式、集群

目录 一、Redis模式二、Redis主从复制2.1 主从复制概述2.2 主从复制2.3 Redis主从复制过程2.4 搭建Redis主从复制2.4-1 环境部署2.4-2 安装Redis2.4-3 修改 Redis 配置文件&#xff08;Master节点操作&#xff09;2.4-4 修改 Redis 配置文件&#xff08;Slave节点操作&#xff…

『手撕 Mybatis 源码』03 - 解析映射配置文件

解析映射配置文件 SQL 映射文件只有很少的几个顶级元素&#xff08;按照定义顺序列出&#xff09; select 元素允许你配置很多属性来配置每条语句的行为细节 <selectid"select"parameterType"int"parameterMap"deprecated"resultType&quo…

Qt之界面 自定义标题栏、无边框、可移动、缩放

实现效果 注意&#xff1a;由于需要调用 Windows 上的头文件与库&#xff0c;所以不能跨平台&#xff0c;只支持 Windows 系统。如果想要跨平台&#xff0c;可以使用鼠标等事件实现&#xff0c;具体百度搜索参考下 自定义标题栏 titleBar.h #ifndef TITLEBAR_H #define TITL…

[Nacos] Nacos Client向Server发送注册请求和心跳请求 (二)

文章目录 1.Nacos Client的自动注册原理和实现2.Naocs Client向Server发送注册请求3.Nacos Client向Server发送心跳请求 Nacos Client的任务: 向Server发送注册请求, 向Server发送心跳请求, Client获取所有的服务, Client定时更新本地服务, Client获取要调用服务的提供者列表 …

Robot Dynamics Lecture Notes学习笔记之关节空间动力学控制

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 关节空间动力学 关节空间动力学控制关节阻抗调节重力补偿逆动力学控制 关节空间动力学控制 目前的工业机器人几乎完全依赖于关节位置控制的概念。它们建立在PID控制器的基础…

MySQL数据库期末实验报告(含实验步骤和实验数据)

MYSQL实验 实验步骤 1.创建数据库salesmanage 2.创建数据表&#xff1a;员工表&#xff0c;部门表&#xff0c;销售表&#xff1b; &#xff08;1&#xff09;员工表&#xff08;(员工号(CHAR)&#xff0c;员工姓名(CHAR)&#xff0c;性别(CHAR)&#xff0c;年龄(INT)&…

前端部署项目后nginx转发接口404(页面正常)

目录 1.前言 2. 场景复现&#xff1a; 3.问题的原因&#xff1a; 4.使用nginx一般要注意的小细节&#xff1a; 1. location / 写在下面&#xff0c;其他的转发如/v1写在上面​编辑 2.如何查看nginx转发请求到哪里了&#xff1f; 3.怎么写自己的前端路径&#xff1f; 5.使…

实验六 自动驾驶建模与仿真

【实验目的】 了解Matlab/Simulink软件环境&#xff0c;熟悉Simulink建模步骤&#xff1b;了解车辆运动控制的基本原理&#xff0c;学会简单的车辆运动控制建模及仿真&#xff1b;了解自动驾驶建模的基本过程&#xff0c;了解典型ADAS系统模型的应用特点。了解自动驾驶相关函数…

【SpringCloud组件——Nacos】

前置准备&#xff1a; 分别提供订单系统&#xff08;OrderService&#xff09;和用户系统&#xff08;UserService&#xff09;。订单系统主要负责订单相关信息的处理&#xff0c;用户系统主要负责用户相关信息的处理。 一、服务注册与发现 1.1、在父工程当中引入Nacos依赖 …

JavaScript实现输入数值判断是否为质数、合数的代码

以下为实现输入数值判断是否为质数、合数的程序代码和运行截图 目录 前言 一、输入数值判断是否为质数、合数 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找&#xff1b; 2.本博文代码可…

通讯录实现的需求分析和架构设计

本文实现的是通讯录产品的需求分析和架构设计&#xff0c;重点在于结构层次的设计&#xff0c;方便代码阅读和维护。 一、通讯录实现的需求分析 1、通讯录的功能清单 添加一个人员打印显示所有人员删除一个人员查找一个人员保存文件加载文件 2&#xff0c;数据存储信息 人员…

实际开发中一些实用的JS数据处理方法

写在开头 JavaScript 是一种脚本语言&#xff0c;最初是为了网页提供交互式前端功能而设计的&#xff0c;而现在&#xff0c;通过 Node.js&#xff0c;JavaScript 还可以用于编写服务器端代码。 JavaScript 具有动态性、基于原型的面向对象特性、弱类型、多范式、支持闭包执行…

Golang每日一练(leetDay0072) 课程表 I\II Course Schedule

目录 1. 课程表 Course Schedule I &#x1f31f;&#x1f31f; 2. 课程表 Course Schedule II &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一…

电子邮件协议(SMTP,MIME,POP3,IMAP)

SMTP 关键词&#xff1a; 电子邮件协议:SMTP简单邮件传输协议&#xff0c;负责将邮件上传到服务器&#xff0c;采用TCP的25端口&#xff0c;C/S工作。仅传送ASCII码文本 详细介绍&#xff1a; SMTP是一种提供可靠且有效的电子邮件传输的协议。SMTP是建立在FTP文件传输服务上…

学系统集成项目管理工程师(中项)系列23b_信息系统集成及服务管理(下)

1. 信息技术服务 1.1. 供方为需方提供如何开发、应用信息技术的服务&#xff0c;以及供方以信息技术为手段提供支持需方业务活动的服务 1.2. 信息技术咨询服务、设计与开发服务、信息系统集成服务、数据处理和运营服务及其他信息技术服务 2. 信息系统审计 2.1. 收集并评估证…

Golang中的协程(goroutine)

目录 进程 线程 并发 并行 协程(goroutine) 使用sync.WaitGroup等待协程执行完毕 多协程和多线程 进程 进程就是程序在操作系统中的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff0c;进程是一个动态概念&#xff0c;是程序在执行过程中分配和管理…