当前位置: 首页 > news >正文

算法36:单调栈结构、子数组最小乘积的最大值问题(力扣1586)----单调栈

单调栈:就是在栈中实现数据的单调性。即从栈底到栈顶,要么递增,要么递减。

那么,使用单调栈,可以解决什么问题呢?

给定一个可能含有重复值的数组arr,i位置的数一定存在如下两个信息

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

2)arr[i]的右侧离i最近并且小于(或者大于)arr[i]的数在哪? 如果想得到arr中所有位置的两个信息,怎么能让得到信息的过程尽量快

题目一:给定一个一维数组,数据都为正整数并且无重复值,要求设计一个O(N)时间复杂度的算法,找出任意位置的数据,左侧小于当前位置最近的数在哪,右侧小于当前数最近的的数在哪?

假设: 这个数组是 {1,3,5,4}。栈的单调性从栈底到栈顶递增。

那么如下:

5
3
1

也就是说,前3个数符合预期的栈的单调性,可以正常的放入栈中。那么,当最后一个数据4想要放入栈中的时候,发现栈顶元素为5,比自己大。直接放入就破坏了栈的单调性了。

1. 我们需要把栈顶元素5弹出,这5就知道右侧小于自己的并且距离最近的数为4,而左侧离自己最近并且小于自己的数为3.

2. 此时,栈顶元素为3,小于4. 那么4直接放入栈顶。整个数组全部结束

4
3
1

3. 栈循环弹出,4是最后一个元素,并且栈具有单调性。因此,弹出4可以知道,左侧离自己最近的数为3. 而右侧没有比自己更小的数

4. 弹出3,左侧比自己小的数是1,而右侧没有比自己小的数

5. 弹出1,此时栈空了。左侧、右侧都没有小于自己的数。

以上一切,只是为了直观的体现栈整个操作流程。而实际的算法设计肯定是不能用数据来直接使用的,而是需要使用每个数据对应的位置,即下标

//无重复元素public int[][] dp(int[] arr){if(arr == null || arr.length == 0) {return null;}int[][] dp = new int[arr.length][arr.length];Stack<Integer> stack = new Stack<>();for (int i = 0; i < arr.length; i++){while (!stack.isEmpty() && arr[stack.peek()] > arr[i]){int cur = stack.pop();// -1代表不存在左侧比cur下标对应的值更小的值int leftIndex = stack.isEmpty() ? -1 : stack.peek();dp[cur][0] = leftIndex;dp[cur][1] = i;}//放入下标stack.push(i);}//栈中剩余元素,保持单调增while (!stack.isEmpty()) {int cur = stack.pop();// -1代表不存在左侧比cur下标对应的值更小的值int leftIndex = stack.isEmpty() ? -1 : stack.peek();dp[cur][0] = leftIndex;//因为单调增、所有右侧不存在比自己还小的值了dp[cur][1] = -1;}return dp;}

题目二:数组存在重复元素,设计一个栈,要求能够快速找到任意位置左、右侧比自己小、位置最近的数据。

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++) { // i -> arr[i] 进栈while (!stack.isEmpty() && arr[stack.peek().get(0)] > arr[i]) {List<Integer> popIs = stack.pop();int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);for (Integer popi : popIs) {res[popi][0] = leftLessIndex;res[popi][1] = i;}}if (!stack.isEmpty() && arr[stack.peek().get(0)] == arr[i]) {stack.peek().add(Integer.valueOf(i));} else {ArrayList<Integer> list = new ArrayList<>();list.add(i);stack.push(list);}}while (!stack.isEmpty()) {List<Integer> popIs = stack.pop();int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);for (Integer popi : popIs) {res[popi][0] = leftLessIndex;res[popi][1] = -1;}}return res;}

题目三:力扣1856. 子数组最小乘积的最大值

https://leetcode.cn/problems/maximum-subarray-min-product/description/

题目详情直接打开连接进行查看,这里直接说解题思路。

1. 给定数组,就存在子数组,并且子数组是连续的

2.子数组中肯定是存在最小值的,也必然会知道子数组累加和。

3. 假设每个数都是最小值,这样就能利用单调栈结构找到左侧、右侧比自己小的位置。那么除了这两个位置以外,中间部分的数据就是自己最小了。利用这个思想,我们来实现代码

数据1354
下标0123
累加和14913

5左侧比自己小的数据为3,对应下标为1;

5右侧比自己小的数据为4,对应下标为3;

也就是说5这个数据想要做最小值,那么下标1到3之间,并且不能包含下标1和下标3的和。

既然不能包含到下标为3的位置,变相的也就是能够包含到下标为2的位置,即累加和为 9 - 4 = 5;

那子数组累加和 * 最小值 =  5 * 5 = 25;

其他的依次类推........

package code04.单调栈_01;import java.util.Stack;/*** 1856. 子数组最小乘积的最大值* https://leetcode.cn/problems/maximum-subarray-min-product/description/*/
public class Code01_MinSumOfSubArr {public int maxSumMinProduct(int[] nums){if (nums == null || nums.length == 0) {return 0;}int size = nums.length;//前缀和数组。 题目规定要使用64位有符号整数保存long[] dp = new long[size];dp[0] = nums[0];for (int i = 1; i < size; i++) {dp[i] = dp[i-1] + nums[i];}long ans = Long.MIN_VALUE;//[0 ......)Stack<Integer> stack = new Stack();for(int i = 0; i < size; i++){while (!stack.isEmpty()&& nums[stack.peek()] >= nums[i]) {//当前正在处理的数下标int cur = stack.pop();long sum = stack.isEmpty() ? dp[i-1] : dp[i-1] - dp[stack.peek()];ans = Math.max(ans, sum * nums[cur]);}//放入下标stack.push(i);}//右侧值越来越大while (!stack.isEmpty()) {//当前正在处理的数下标int cur = stack.pop();long sum = stack.isEmpty() ? dp[size-1] : (dp[size-1] - dp[stack.peek()]);ans = Math.max(ans, sum * nums[cur]);}return (int) (ans % 1000000007);}public static void main(String[] args){int[] nums = {3,1,5,6,4,2};Code01_MinSumOfSubArr test = new Code01_MinSumOfSubArr();System.out.println(test.maxSumMinProduct(nums));}
}

此处可能会有疑问,此处使用的是无重复元素构造单调栈的算法,这一题不需要考虑重复元素的情况吗?举个例子,假如数组为 {1,2,3,2}

栈中放入1,2 3.  当放入最后一个2的时候,会把栈中的3和2弹出,并且把最后一个2入栈。 而最后一个2右侧没有比他小的值,左侧比他小的值为1,对应的下标为0. 也就是说从下标0到最后一个2的位置,此时最后一个2是最小值。当然,下标0处的1是不包含在内的。

也就是说,重复元素具有连通性,很多时候是不需要考虑重复元素的情况的。

相关文章:

算法36:单调栈结构、子数组最小乘积的最大值问题(力扣1586)----单调栈

单调栈&#xff1a;就是在栈中实现数据的单调性。即从栈底到栈顶&#xff0c;要么递增&#xff0c;要么递减。 那么&#xff0c;使用单调栈&#xff0c;可以解决什么问题呢&#xff1f; 给定一个可能含有重复值的数组arr&#xff0c;i位置的数一定存在如下两个信息 1&#x…...

django mysql in 有序返回

from django.db.models import * ordering f"FIELD(id, {,.join([str(_) for _ in ids])})" # 默认就按照算法返回的 id 排序p_data_result PeptidesDataResult.objects.using("polypeptide").filter(id__inids).values().extra(select{ordering: orderi…...

c++24.1.26嵌套if语句

嵌套if语句&#xff1a;if语句中的if语句 实践&#xff1a;...

机器学习--基础概念(二)

1.分类算法 分类算法是有监督学习的一个核心问题&#xff0c;他从数据中学习一个分类决策函数或分类模型&#xff0c;对新的输入进行预测&#xff0c;输出变量取有限个离散值。 以下是一些常见的分类算法&#xff1a; 逻辑回归 (Logistic Regression): 用于二分类问题&#x…...

Ubuntu20.04 安装 ROS noetic + MAVROS

本文在 AlphaCatOvO【ROS】在 Ubuntu 20.04 安装 ROS 的详细教程 基础上&#xff0c;根据实际安装经验&#xff0c;稍微进行补充。 一、安装Ubuntu20.04 假设已经正确安装。 二、安装 ROS noetic 2.1 换源 执行 sudo apt update sudo mv /etc/apt/sources.list /etc/apt/…...

【数学笔记】一元n次不等式,分式不等式,绝对值不等式

不等式 基本性质 一元n次不等式一元二次不等式一元高次不等式分式不等式绝对值不等式 基本性质 性质 a > b ⇔ b < a a>b\Leftrightarrow b<a a>b⇔b<a a > b , b > c ⇒ a > c a>b,b>c\Rightarrow a>c a>b,b>c⇒a>c a > b ,…...

转载-android性能优化

android性能优化 Reason: Broadcast of Intent { actandroid.intent.action.TIME_TICK ActivityManager: ANR in com.***.*** PID: 16227 Reason: Broadcast of Intent { actandroid.intent.action.TIME_TICK flg0x50000014 (has extras) }有那么一段时间我被这个ANR折磨到每…...

笔记 | Clickhouse命令行查询

在 ClickHouse 中&#xff0c;可以使用命令行客户端执行查询。默认情况下&#xff0c;ClickHouse 的命令行客户端称为 clickhouse-client。下面是一些基本的步骤和示例&#xff0c;用于使用 clickhouse-client 进行查询。 首先&#xff0c;需要确保已经安装了 ClickHouse 服务…...

Dockerfile-xxxx

1、Dockerfile-server FROM openjdk:8-jdk-alpine WORKDIR /app COPY . . CMD java -Xms1536M -Xmx1536M -XX:UseG1GC -jar -Dlog4j2.formatMsgNoLookupstrue -Dloader.pathresources,lib -Duser.timezoneGMT-05 /app/server-main-1.0.0.jar 2、Dockerfile-bgd #FROM openjdk…...

Vue中的$attrs

今天产品经理要求做保留某组件全部功能&#xff0c;还要在它的基础上增加东西。如果不嫌麻烦的话就笨办法&#xff0c;但是想一下怎么只用少量代码高效的二次封装组件呢 Vue中的$attrs 在 Vue2 中&#xff0c;attr 是指组件接收的 HTML 特性(attribute)&#xff0c;通过 prop…...

使用阿里云的oss对象存储服务实现图片上传(前端vue后端java详解)

一&#xff1a;前期准备&#xff1a; 1.1&#xff1a;注册阿里云账号&#xff0c;开启对象存储oss功能&#xff0c;创建一个bucket&#xff08;百度教程多的是&#xff0c;跟着创建一个就行&#xff0c;创建时注意存储类型是标准存储&#xff0c;读写权限是公共读&#xff09;…...

python实例100第32例:使用a[::-1]按相反的顺序输出列表的值

题目&#xff1a;按相反的顺序输出列表的值。 程序分析&#xff1a; a[n:-n]作用是去除前n个元素和末n个元素a[-n]作用是取倒数第n个元素a[:-n]的作用是去除后n个元素a[:&#xff1a;-1]的作用是将所有元素逆序排列a[n:&#xff1a;-1] 的作用是从第n个元素截取后逆序排列 程序…...

python执行脚本的时候获取输入参数

当我们执行脚本的时候&#xff0c;通常都会执行 python test.py -i xxx -o xxx&#xff0c;这里的 -i 和 -o 都是输入参数&#xff0c;这到底是怎么传递的呢&#xff1f; 本文纯粹记录一下 import argparseif __name__ __main__:print("hello")# 创建AugumentParser…...

Halcon指定区域的形状匹配

Halcon指定区域的形状匹配 文章目录 Halcon指定区域的形状匹配1.在参考图像中选择目标2.创建模板3.搜索目标 在这个实例中&#xff0c;会介绍如何根据选定的ROI选择合适的图像金字塔参数&#xff0c;创建包含这个区域的形状模板&#xff0c;并进行精确的基于形状模板的匹配。最…...

Linux——常用命令

1、命令的基本格式 对服务器来讲&#xff0c;图形界面会占用更多的系统资源&#xff0c;而且会安装更多的服务、开放更多的端口&#xff0c;这对服务器的稳定性和安全性都有负面影响。其实&#xff0c;服务器是一个连显示器都没有的家伙&#xff0c;要图形界面干什么&#xff…...

外包干了2个月,技术反而退步了...

先说一下自己的情况&#xff0c;本科生&#xff0c;19年通过校招进入广州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…...

洛谷C++简单题练习day6—P1830 城市轰炸

day6--P1830 城市轰炸--1.26 习题概述 题目背景 一个大小为 nm 的城市遭到了 x 次轰炸&#xff0c;每次都炸了一个每条边都与边界平行的矩形。 题目描述 在轰炸后&#xff0c;有 y 个关键点&#xff0c;指挥官想知道&#xff0c;它们有没有受到过轰炸&#xff0c;如果有&a…...

【linux-interconnect】What NVIDIA MLNX_OFED is?

NVIDIA MLNX_OFED Documentation v23.07 - NVIDIA Docs 文章目录 What NVIDIA MLNX_OFED is&#xff1f;Overview[Software Download](https://docs.nvidia.com/networking/display/mlnxofedv23070512#src-2396583107_NVIDIAMLNX_OFEDDocumentationv23.07-SoftwareDownload) Wh…...

Unity开发中的XML注释

在Unity开发中&#xff0c;XML注释主要用于C#脚本的注释&#xff0c;以帮助生成代码文档和提供IntelliSense功能。以下是一些关于如何使用XML注释的技巧&#xff1a; 创建注释&#xff1a; 在C#中&#xff0c;XML注释是由///或/**...*/开始的。例如 /// <summary> /// 这…...

[MQ]常用的mq产品图形管理web界面或客户端

一、MQ介绍 1.1 定义 MQ全称为Message Queue&#xff0c;消息队列是应用程序和应用程序之间的通信方法。 如果非要用一个定义来概括只能是抽象出来一些概念&#xff0c;概括为跨服务之间传递信息的软件。 1.2 MQ产品 较为成熟的MQ产品&#xff1a;IBMMQ&#xff08;IBM We…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

LLMs 系列实操科普(1)

写在前面&#xff1a; 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容&#xff0c;原视频时长 ~130 分钟&#xff0c;以实操演示主流的一些 LLMs 的使用&#xff0c;由于涉及到实操&#xff0c;实际上并不适合以文字整理&#xff0c;但还是决定尽量整理一份笔…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist

现象&#xff1a; android studio报错&#xff1a; [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决&#xff1a; 不要动CMakeLists.…...