单调栈练习(五)— 子数组的最小值之和

题目
同样的LeetCode原题:题目链接

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

由于答案可能很大,因此 返回答案模 10^9 + 7 。

在这里插入图片描述

思路

暴力解
先来说暴力解的思路:
三层for循环,找到每一个子数组中的最小值,0-0,0-1,0-2 … 0-N。1-1,1-2,1-3…1-N。并将每一个子数组的最小值相加即可,时间复杂度 O ( N 3 ) O(N^3) O(N3)

在这里插入图片描述

代码

public static int subArrayMinSum2(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }

        int N = arr.length;
        int ans = 0;
        for (int i = 0; i < N; i++) {
            for (int j = i; j < N; j++) {
                int min = arr[i];
                for (int k = i + 1; k <= j; k++) {
                    min = Math.min(min, arr[k]);
                }
                ans += min;
            }
        }
        return ans;
    }

优化的普通解

优化的方法是根据题目中给定的 arr[],生成对应的 left[] 和 right[]。

left[]:left[i] = x ,表示左侧最近 <= arr[i] 的位置在 x(x表示对应的索引)
right[]:right[i] = y,表示右侧最近 < arr[i] 的位置在y(y表示对应的索引)

生成 left[] 和 right[]的作用在于,根据当前子数组中最小值 arr[i] 在 left[]、right[]中获取左右最小且近的边界值后,直接进行计算求出以 arr[i] 作为子数组最小值的累加和,优化后的时间复杂度是 O ( N ) O(N) O(N)

来看下面的例子:当前 arr[8] = 7 ,找到左侧最近且小的值在是4位置的3,右侧最近且小的值是12位置4。
在这里插入图片描述

此时,以7作为最小值的子数组范围是 5 ~ 11,并且此范围内所有值都是 >= 7 的,但是因为要以7作为最小值,所以5 ~ 11范围内所求的子数组要把8位置的7涵盖进去。此时利用 left[8] = 4 、right[8] = 12, (8 - 4)x(12 - 8)x (arr[8]) = 112,就是7作为最小值的时候所有子数组最小值的累加和。

需要注意的是:如果是普通的无重复值数组,那么很好处理,但如果数组中有重复值,就需要额外注意!!!

举例:
在这里插入图片描述
碰上有重复值的数组,要稍微多考虑一些,就是左右边界的设定选择改怎么选?本题中答案选用的是第一种

两种选择的不同其实目的都是一个,那就是抛去重复值的计算

比如说:
此时以4位置的3作为最小值,如果依然是从1位置开始枚举每个子数组 1 - 4 、 1 - 5 … 1 - 11,那等到以7位置的3作为最小值时呢? 此时如果再从1 - 7、 1 - 8 … 1 - 11。那么和之前计算的子数组最小值就包含了重复的部分。所以要去重。

代码

构建完成后,遍历一遍 arr[] , 将每一个值都作为子数组的最小值,并根据 left[]、right[] 算出子数组的个数,个数 * 最小值,不就是我们想要的答案,再将求出来的每一个结果进行累加。

 public static int subArrayMinSum2(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
   
        int[] left = leftNearLessEqual1(arr);
        int[] right = rightNearLess1(arr);
        int ans = 0;
        for (int i = 0; i < arr.length; i++) {
            int start = i - left[i];
            int end = right[i] - i;
            ans += start * end  * arr[i];
        }
        return ans;
    }

    public static int[] rightNearLess1(int[] arr) {

        int N = arr.length;
        int[] right = new int[N];
        int ans;

        for (int i = 0; i < N; i++) {
            ans = N;
            for (int j = i + 1; j < N; j++) {
                if (arr[j] < arr[i]) {
                    ans = j;
                    break;
                }
            }
            right[i] = ans;
        }
        return right;
    }

    public static int[] leftNearLessEqual1(int[] arr) {
        int N = arr.length;
        int[] left = new int[N];
        int ans;
        for (int i = N - 1; i >= 0; i--) {
            ans = -1;
            for (int j = i - 1; j >= 0; j--) {
                if (arr[j] <= arr[i]) {
                    ans = j;
                    break;
                }
            }
            left[i] = ans;
        }
        return left;
    }

单调栈
单调栈其实就是将构建left[]、right[]的过程加快了,其逻辑是不变的。
这里的单调栈是自己用数组实现,没有用系统提供的Stack,但是照比系统提供的性能要更好一些。

 public static int sumSubarrayMins(int[] arr) {

       if (arr == null || arr.length == 0) {
           return 0;
       }

       int[] stack = new int[arr.length];
       int[] left = leftNearLessEqual2(arr, stack);
       int[] right = rightNearLess2(arr, stack);
       long ans = 0;

       for (int i = 0; i < arr.length; i++) {
           long start = i - left[i];
           long end = right[i] - i;
           ans += (start * end) * arr[i];
           ans %= 1000000007;
       }
       return (int)ans;
   }

    public static int[] rightNearLess2(int[] arr,int[] stack) {

        int N = arr.length;
        int[] right = new int[N];
        int size = 0;

        for (int i = 0; i < N; i++) {
          while (size != 0 && arr[i] < arr[stack[size - 1]]){
              right[stack[--size]] = i;
          }
          stack[size++] = i;
        }

        while (size != 0){
            right[stack[--size]] = N;
        }

        return right;
    }

    public static int[] leftNearLessEqual2(int[] arr,int[] stack) {
        int N = arr.length;
        int[] left = new int[N];
        int size = 0;
        for (int i = N - 1; i >= 0; i--) {
            //stack[size - 1] :当前最后加入到数组中元素即为栈顶元素
            //stack[--size] :出栈操作,弹出栈顶元素并且大小 - 1,后加入的元素要覆盖当前位置
            while (size != 0 && arr[i] <= arr[stack[size - 1]]){
                //当前 i 位置 使栈顶元素出栈,所以 left[栈顶元素左侧小且近] = 当前下标 i
                left[stack[--size]] = i;
            }
            //入栈操作:存储当前元素索引
            stack[size++] = i;
        }
        while (size != 0){
            left[stack[--size]] = -1;
        }
        return left;
    }

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

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

相关文章

别再给自己的创业失败找借口了,什么都有你还创什么业?2024普通人如何创业,2024适合普通人的创业项目

说起创业&#xff0c;大家都是满腹牢骚&#xff0c;抱怨现在阶层固化&#xff0c;没有机会&#xff0c;自己也没有钱&#xff0c;没有资源。反正就是给自己创业失败找借口。 但是马云曾经表示&#xff0c;钱是最容易得到的东西&#xff0c;如果自己一开始就有钱&#xff0c;那…

C++特殊类设计类型转换

一、特殊类设计 在普通类的设计基础上&#xff0c;提出一些限制条件设计的类就是特殊类。 1、请设计一个类&#xff0c;不能被拷贝 拷贝只会放生在两个场景中&#xff1a;拷贝构造函数以及赋值运算符重载&#xff0c;因此想要让一个类禁止拷贝&#xff0c; 只需让该类不能调…

Codeforces Round 919 (Div. 2) A~E

A. Satisfying Constraints(模拟) 题意&#xff1a; 给出 n n n个限制条件&#xff0c;问有多少个数字 k k k同时满足这些限制条件。 限制条件分为以下三种&#xff1a; k k k必须大于等于给出的一些数字 x x x k k k必须小于等于给出的一些数字 x x x k k k不能与给出的…

Go新项目-为何选Gin框架?(0)

先说结论&#xff1a;我们选型Gin框架 早在大概在2019年下旬&#xff0c;由于内部一个多线程上传的需求&#xff0c;考虑到Go协程的优势&#xff1b; 内部采用Gin框架编写了内部的数据上传平台BAP&#xff0c;采用GinVue开发&#xff0c;但前期没考虑到工程化思维&#xff0c;导…

【linux】终端发送网络请求与文件下载

发送网络请求 linux的终端中发送网络请求可以使用curl命令。 语法&#xff1a; curl [url] 但是他返回的是html代码&#xff0c;因为在终端中&#xff0c;他无法像浏览器中一样把访问到的html代码渲染成我们访问的页面&#xff0c;所以我们只能拿到他的源码。 访问CSDN - 专…

线性表的应用 | 线性表的合并

线性表的合并 #include <iostream> using namespace std;#define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFEASIBLE -1 #define OVERFLOW -2typedef int Status;// 定义单链表 typedef struct LNode {int data;struct LNode *next; }LNode, *…

stack,queue和prioriy_queue

MySTL stack和queue template <class T, class Container deque<T> > class queue;template <class T, class Container deque<T> > class stack;选择适配器的宗旨是要能达到预想的功能 queue——只能使用list和deque stack——可以使用vector和…

【开源】基于JAVA的康复中心管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 普通用户模块2.2 护工模块2.3 管理员模块 三、系统展示四、核心代码4.1 查询康复护理4.2 新增康复训练4.3 查询房间4.4 查询来访4.5 新增用药 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的康复中…

试用清华Chatglm智能体

清华AI平台&#xff0c;感觉在见过的国内AI平台中做的是比较优秀的&#xff0c;目前该平台提供的智能体功能感觉更智能或者说更傻瓜式一些。定义可以定义专属智能体&#xff0c;这些智能体是自己想要的网络上的汇集处理后的信息&#xff0c;或者是绘画或者是编写某个方面的代码…

16.桥接模式

桥接模式 介绍 桥接模式是一种结构型设计模式&#xff0c;它通过将抽象部分与实现部分分离&#xff0c;使它们可以独立变化。这种模式通过组合的方式来实现&#xff0c;而不是继承。桥接模式通过将抽象和实现解耦&#xff0c;从而实现抽象和实现的分离&#xff0c;使得系统更加…

数字人系统OEM流程:部署AI数字人系统源码需要注意哪些?

随着数字化技术的不断发展&#xff0c;数字人SaaS系统源码的部署已经成为许多企业关注的焦点。数字人SaaS系统源码的部署可以帮助企业降低成本、提高运营效率&#xff0c;为企业提供更加高效的服务。然而&#xff0c;在部署数字人SaaS源码时&#xff0c;有一些须知事项需要我们…

SpringAOP-说说 JDK动态代理和 CGLIB 代理

Spring 的 AOP 是通过动态代理来实现的&#xff0c;动态代理主要有两种方式 JDK 动态代理和 Cglib 动态代理&#xff0c;这两种动态代理的使用和原理有些不同。 JDK 动态代理 Interface&#xff1a;JDK动态代理是基于接口的代理&#xff0c;它要求目标类实现一个接口。Invoca…

内存四区图练习

带着白卡去旅行 绘制图中三种情况的内存四区图 一个实参 一个形参 取地址 通过指针修改变量 返回 多级指针的训练 #define _CRT_SECURE_NO_WARNINGS #include<stdlib.h> #include<stdio.h> #include<string.h> #include<math.h>int getMem(char***p3,…

模拟记事本

1. 模拟记事本 设计一个记事本 &#xff08;1&#xff09;更改字体颜色 &#xff08;2&#xff09;更改字体大小 &#xff08;3&#xff09;新建记事本 &#xff08;4&#xff09;查找记事本中的数据 &#xff08;5&#xff09;设置消息提示 &#xff08;6&#xff09;设置粘贴…

蓝莓产量预测(R语言版)

数据描述 字段名 描述 字段名 描述 id 蓝莓唯一标识 MinOfUpperTRange 花期内最高温带日平均气温的最低记录, Clonesize 蓝莓克隆平均大小 AverageOfUpperTRange 花期内最高温带日平均气温, Honeybee 蜜蜂密度 MaxOfLowerTRange 花期内最低温带日平均气温的最…

GEE:机器学习分类中每个类别的概率图像可视化

作者:CSDN @ _养乐多_ 在 Google Earth Engine(GEE) 中应用机器学习分类器进行多分类时,有一个需求是想知道每个像素对于每个类别的分类概率。 比如在进行随机森林分类时,每个决策树会生成一个类别,通过投票选择票数最多的类别作为最终分类。除了最终分类结果,其他类别…

【昕宝爸爸小模块】浅谈之创建线程的几种方式

➡️博客首页 https://blog.csdn.net/Java_Yangxiaoyuan 欢迎优秀的你&#x1f44d;点赞、&#x1f5c2;️收藏、加❤️关注哦。 本文章CSDN首发&#xff0c;欢迎转载&#xff0c;要注明出处哦&#xff01; 先感谢优秀的你能认真的看完本文&…

开源鸿蒙适配芯片到底都做了哪些工作?

随着智能设备市场的不断扩大和技术的进步&#xff0c;鸿蒙操作系统成为了备受瞩目的开源项目。作为一个全场景智能生态的基础&#xff0c;鸿蒙不仅仅是一个操作系统&#xff0c;还涉及到硬件层面的适配。然而&#xff0c;开源鸿蒙芯片适配并非易事&#xff0c;面临着一些难点和…

国内外好用的 LLM 列表

视频来源&#xff1a;https://www.bilibili.com/video/BV1c64y157Qm/?vd_source1e841703c91b5b77fd20e5707bae49d2 下图是测试括号闭合能力的得分

PointMixer: MLP-Mixer for Point Cloud Understanding

Abstract MLP-Mixer 最近崭露头角,成为对抗CNNs和Transformer领域的新挑战者。尽管相比Transformer更为简单,但通道混合MLPs和令牌混合MLPs的概念在图像识别任务中取得了显著的性能。与图像不同,点云本质上是稀疏、无序和不规则的,这限制了直接将MLP-Mixer用于点云理解。为…