算法38:子数组的最小值之和(力扣907题)----单调栈

题目:

给定一个整数数组 arr,找到 min(b) 的总和,其中 b 的范围为 arr 的每个(连续)子数组。

示例 1:

输入:arr = [3,1,2,4]
输出:17
解释:
子数组为 [3],[1],[2],[4],[3,1],[1,2],[2,4],[3,1,2],[1,2,4],[3,1,2,4]。 
最小值为 3,1,2,4,1,1,2,1,1,1,和为 17。

示例 2:

输入:arr = [11,81,94,43,3]
输出:444

分析:

很显然,需要分析每个子数组的最小值。而单调栈结构就可以帮助我们找到每个元素左、右侧比自己小的最近位置。也就是说,在一定范围内,数组中每个元素都可以作为子数组的最小值。

假设:

1. 假设数组为1,那么子数组最小值就为1.

2. 假设数组为{1,2} 那么子数组就可以为{1} 和 {1,2}

3. 假设数组为{1,2,3} 那么子数组就可以为{1}、{1,2} 和 {1,2,3}

总结:如果以1为子数组最小值,那么1后面出现的比1大的数,有几个数,子数组就为 n + 1。

数组{1,2,3}中,以1为最小值,那么1后面有2个数比1大,子数组就为 2 + 1 = 3个。

假设数组为{1,2,3}, 现在在最小值1前面加一个数2, 变成{2,1,2,3}.  那么子数组就为

{2,1}、{2,1,2} 、{2,1,2,3}

{1}、{1、2}、{1,2,3}

我们发现,子数组数量是原始数组{1,2,3}的2倍,即 2 * 3 = 6 个。

假设,我们在现有的数组中,前面在添加一个元素3. 变成{3,2,1,2,3}.  那么子数组为:

{3,2,1} 、{3,2,1,2} 、{3,2,1,3}

{2,1}、{2,1,2} 、{2,1,2,3}

{1}、{1、2}、{1,2,3}

我们发现,子数组数量是原始数组{1,2,3}的3倍,即 3 * 3 = 9 个.

总结:如果以1为子数组最小值,那么1前面出现的比1大的数,有几个,那么就是 (N +1)* 原始个数。

 以{3,2,1,2,3}为例子。

如果以1为最小值,1后面有2个数比1大,得到 2+1 = 3; 1前面有2个比1大,得到2+1= 3; 3*3 =9; 如果以1为最小值,那么一共有9个子数组。

如果以下标为1的2为最小值,可得 (1+1)* (0+1) = 2; 即如果以下标为1的2值为最小值,子数组有2个。即 {3,2} 和 {2}

如果以下标为3的位置的2值为最小值,前一个位置为1,即0个。前方0个比自己大的;后面1个3比自己大,可得 (0+1)*(1+1) = 2个;即{2}和{2、3}

依次类推,可以得到全部结果.

package code04.单调栈_01;import java.util.Stack;/*** 力扣力扣907题: 子数组的最小值*  https://leetcode.com/problems/sum-of-subarray-minimums/*/
public class Code04_SumOfMinValueInArray {public int sumSubarrayMins(int[] arr){int[][] dp = dp(arr);//long比int能存更长的数据long ans = 0;for (int i = 0; i < arr.length; i++) {//当前数int cur = arr[i];//左侧小于等于当前数的个数int left = i - dp[i][0];//右侧小于等于当前数的个数int right = dp[i][1] - i;ans +=  left * right * (long)cur;ans %= 1000000007;}return (int) ans;}//单调栈,统计出每个位置左、右侧比当前数小的位置public int[][] dp(int[] arr){if(arr == null || arr.length == 0) {return null;}//当前业务需要统计出2列信息, 即左侧比自己小的位置,右侧比自己小的位置int[][] dp = 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]){int cur = stack.pop();// -1代表不存在左侧比cur下标对应的值更小的值int leftIndex = stack.isEmpty() ? -1 : stack.peek();dp[cur][0] = leftIndex;dp[cur][1] = i;}//放入下标stack.push(i);}int rightIndex = arr.length;//栈中剩余元素,保持单调增while (!stack.isEmpty()) {int cur = stack.pop();// -1代表不存在左侧比cur下标对应的值更小的值int leftIndex = stack.isEmpty() ? -1 : stack.peek();dp[cur][0] = leftIndex;//因为单调增、所有右侧不存在比自己还小的值了dp[cur][1] = rightIndex;}return dp;}public static void main(String[] args) {Code04_SumOfMinValueInArray ss = new Code04_SumOfMinValueInArray();int[] aa = {3,1,2,4};System.out.println(ss.sumSubarrayMins(aa));}
}

虽然测试通过了,但是执行用时99ms,只击败了17%的用户,说明代码不够优秀。

技巧就是,将java原有的Stack替换成自己实现的数组。因为自己实现的数组是固定的,而Stack是需要不断经过扩容的。这样优化,效果很明显。

package code04.单调栈_01;import java.util.Stack;/*** 力扣力扣907题: 子数组的最小值*  https://leetcode.com/problems/sum-of-subarray-minimums/*/
public class Code04_SumOfMinValueInArray_opt {public int sumSubarrayMins(int[] arr){int[][] dp = dp(arr);//long比int能存更长的数据long ans = 0;for (int i = 0; i < arr.length; i++) {//当前数int cur = arr[i];//左侧小于等于当前数的个数int left = i - dp[i][0];//右侧小于等于当前数的个数int right = dp[i][1] - i;ans +=  left * right * (long)cur;ans %= 1000000007;}return (int) ans;}//单调栈,统计出每个位置左、右侧比当前数小的位置public int[][] dp(int[] arr){if(arr == null || arr.length == 0) {return null;}//当前业务需要统计出2列信息, 即左侧比自己小的位置,右侧比自己小的位置int[][] dp = new int[arr.length][2];int[] stack = new int[arr.length];int stackSize = 0;for (int i = 0; i < arr.length; i++){while (stackSize != 0 && arr[stack[stackSize-1]] > arr[i]){int cur = stack[--stackSize];// -1代表不存在左侧比cur下标对应的值更小的值int leftIndex = stackSize == 0 ? -1 : stack[stackSize - 1];dp[cur][0] = leftIndex;dp[cur][1] = i;}//放入下标stack[stackSize++] = i;}int rightIndex = arr.length;//栈中剩余元素,保持单调增while (stackSize != 0) {int cur = stack[--stackSize];// -1代表不存在左侧比cur下标对应的值更小的值int leftIndex = stackSize == 0 ? -1 : stack[stackSize - 1];dp[cur][0] = leftIndex;//因为单调增、所有右侧不存在比自己还小的值了dp[cur][1] = rightIndex;}return dp;}public static void main(String[] args) {Code04_SumOfMinValueInArray_opt ss = new Code04_SumOfMinValueInArray_opt();int[] aa = {3,1,2,4};int[][] dp = ss.dp(aa);for (int i = 0; i < dp.length; i++) {System.out.println("当前下标 :" + i + ", 左侧小值: " + dp[i][0] + ", 右侧小值: " + dp[i][1]);}System.out.println(ss.sumSubarrayMins(aa));}
}

优化完以后,效果很明显: 

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

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

相关文章

go语言(十八)---- goroutine

一、goroutine package mainimport ("fmt""time" )func main() {//用go创建承载一个形参为空&#xff0c;返回值为空的一个函数go func() {defer fmt.Println("A.defer")func() {defer fmt.Println("B.defer")//退出当前goroutinefmt…

Ubuntu添加AppImage到桌面及应用程序菜单

将AppImage添加到桌面&#xff0c;以PicGo为例 效果&#xff1a; 在桌面创建PicGo.desktop文件&#xff0c;输入以下内容&#xff1a; [Desktop Entry] EncodingUTF-8 TypeApplication #应用名称 NamePicGo #图标路径 Icon/usr/local/AppImage/icons/PicGo.png #启动是否开启…

谁管谁叫爹

L1-8 谁管谁叫爹 分数 20 作者 陈越 单位 浙江大学 《咱俩谁管谁叫爹》是网上一首搞笑饶舌歌曲&#xff0c;来源于东北酒桌上的助兴游戏。现在我们把这个游戏的难度拔高一点&#xff0c;多耗一些智商。…

探索Pyecharts之美-绘制多彩旭日图的艺术与技巧【第37篇—python:旭日图】

文章目录 引言准备工作绘制基本旭日图调整颜色和样式添加交互功能定制标签和标签格式嵌套层级数据高级样式与自定义进阶主题&#xff1a;动态旭日图数据源扩展&#xff1a;外部JSON文件总结 引言 数据可视化在现代编程中扮演着重要的角色&#xff0c;而Pyecharts是Python中一个…

JAVA编程题之用户登录,用户信息存储在本地文件

实现用户登录&#xff1a;键盘输入要登录的用户名与密码 properties类型文件常在框架内用作配置文件. public static void main(String[] args) throws Exception {FileInputStream fis new FileInputStream("user.properties");Properties properties new Prope…

为什么 FPGA 比 CPU 和 GPU 快?

FPGA、GPU 与 CPU——AI 应用的硬件选择 现场可编程门阵列 (FPGA) 为人工智能 (AI) 应用带来许多优势。图形处理单元 (GPU) 和传统中央处理单元 (CPU) 相比如何&#xff1f; 人工智能&#xff08;AI&#xff09;一词是指能够以类似于人类的方式做出决策的非人类机器智能。这包…

k8s 进阶实战笔记 | Pod 创建过程详解

Pod 创建过程详解 ​ 初始状态0 controller-manager、scheduler、kubelet组件通过 list-watch 机制与 api-server 通信并检查资源变化 第一步 用户通过 CLI 或者 WEB 端等方式向 api-server 发送创建资源的请求&#xff08;比如&#xff1a;我要创建一个replicaset资源&…

多表查询练习题

student表: score表: 向student表插入记录: 向score表插入记录: 1.查询student表的所有记录 SELECT * FROM student;2.查询student表的第2条到4条记录 SELECT * FROM student LIMIT 1,3;3.从student表查询所有学生的学号&#xff08;id&#xff09;、姓名&#xff08;name&…

【复现】Laykefu客服系统后台漏洞合集_29

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 2. 漏洞二&#xff1a; 3. 漏洞三&#xff1a; 4. 漏洞四&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 Laykefu客服系统是thinkphp5Gatewayworker搭建的web客服…

【Linux】:线程安全的单例模式

线程安全的单例模式 一.STL和智能指针的安全二.单例模式1.基本概念2.懒汉和饿汉的实现方式 三.常见的其它锁四.读者写者模型 一.STL和智能指针的安全 1.STL中的容器是否是线程安全的? 不是. 原因是, STL 的设计初衷是将性能挖掘到极致, 而一旦涉及到加锁保证线程安全, 会对性…

router4j--SpringCloud动态路由利器

前言 本文介绍Java的动态路由中间件&#xff1a;router4j。router4j用于SpringCloud项目&#xff0c;它可以将某个url请求路由到指定的机器上&#xff0c;也可以将所有请求强制转到指定机器。 问题描述 Java后端在开发SpringCloud项目时如果同一个应用起了多个实例&#xff…

【K12】tk窗口+plt图像功能-学习物理中的串并联研究【附源码说明】

程序源码 import tkinter as tk import matplotlib.pyplot as plt# 初始化 matplotlib 的字体设置 plt.rcParams[font.family] SimHei# 计算串联电路的函数 def calculate_series():try:# 获取用户输入的电阻值并转换为浮点数r1 float(entry_r1.get())r2 float(entry_r2.ge…

第二篇:数据结构与算法-链表

概念 链表是线性表的链式存储方式&#xff0c;逻辑上相邻的数据在计算机内的存储位置不必须相邻&#xff0c; 可以给每个元素附加一个指针域&#xff0c;指向下一个元素的存储位 置。 每个结点包含两个域&#xff1a;数据域和指针域&#xff0c;指针域存储下一个结点的地址&…

C/C++ 跨文件共享全局变量

目录 效果 项目 代码 下载 为了实现跨文件共享全局变量&#xff0c;我们可以使用 extern 关键字。extern 关键字用于声明一个变量&#xff0c;该变量在其他地方已经定义。它告诉编译器这个变量在其他文件中已经定义了&#xff0c;不需要重新分配内存空间&#xff0c;只需要…

C语言-指针的基本知识(上)

一、关于内存 存储器&#xff1a;存储数据器件 外存 外存又叫外部存储器&#xff0c;长期存放数据&#xff0c;掉电不丢失数据 常见的外存设备&#xff1a;硬盘、flash、rom、u盘、光盘、磁带 内存 内存又叫内部存储器&#xff0c;暂时存放数据&#xff0c;掉电数据…

蓝桥杯-sort排序(上)

sort排序 &#x1f388;1.算法说明&#x1f388;2.例题&#x1f52d;2.1例题一&#x1f52d;2.2例题二&#x1f52d;2.3例题三&#x1f52d;2.4例题四&#x1f52d;2.5例题五&#x1f52d;2.6例题六 &#x1f388;1.算法说明 &#x1f50e;对于一个数组&#xff0c;通过对数组中…

Spring Security 存储密码之 JDBC

Spring Security的JdbcDaoImpl实现了UserDetailsService接口,通过使用JDBC提供支持基于用户名和密码的身份验证。 JdbcUserDetailsManager扩展了JdbcDaoImpl,通过UserDetailsManager接口提供UserDetails的管理功能。 当Spring Security配置为接受用户名/密码进行身份验证时,…

南卡Neo2评测:实力诠释骨传导耳机全能旗舰,细节展现匠心之作

前段时间朋友让我帮他寻找一款佩戴舒适、音质体验好的蓝牙耳机&#xff0c;因为比较忙所以一直把这件事搁置了&#xff0c;刚好这两天比较闲&#xff0c;所以也是在综合个人的经验和目前较为热门的一些品牌款式&#xff0c;决定帮他寻找一款骨传导耳机&#xff0c;因为骨传导耳…

JVM-字节码应用

一、字节码的应用远超你的想象 二、ASM介绍与读取字节码实战 用CoreAPI解析和TreeAPI都能做字节码解析&#xff0c;区别&#xff0c;TreeAPI必须读取完整字节码信息&#xff0c;才能做解析。 下面代码&#xff0c;使用CoreAPI做解析&#xff1a; package asm;public class MyM…

[已解决]504 Gateway Time-out 网关超时

文章目录 问题&#xff1a;504 Gateway Time-out 504 Gateway Time-out 网关超时思路解决 问题&#xff1a;504 Gateway Time-out 504 Gateway Time-out 网关超时 思路 网上的常规思路是修改nginx配置文件,增加请求执行时间,试过没有用 keepalive_timeout 600; fastcgi_con…