Java前缀和算法

一.什么是前缀和算法

通俗来讲,前缀和算法就是使用一个新数组来储存原数组中前n-1个元素的和(如果新数组的当前元素的下标为n,计算当前元素的值为原数组中从0到n-1下标数组元素的和),可能这样讲起来有点抽象,我们举一个例子对其进行说明:给定一个数组nums[],我们创建一个大小为nums.length+1的求和数组sums[],对sum[]数组进行遍历:sum[i]=nums[0]+nums[1]+...+nums[i-1](相当于nums[i-1]+sum[n-1]),比如存在int[]nums={1,2,3,4},对应的前缀和数组的值为sum={0,1,3,6,10}

二.前缀和算法相对于滑动窗口具有哪些不可替代的优势及前缀和算法对算法题目的优化策略

 我们在前面讲述的前缀和算法,可能很多小伙伴对其的特点也有所察觉:发现其与我们上一节讲到的滑动窗口有些类似,我们结合下面这道题目,讲讲前缀和算法对于如下这种题目而言,具有怎样的解题优势

对于滑动窗口一般的解题思路如下:如果当前窗口的元素大于预期条件的元素时,我们将窗口的左指针向右移动;如果当前窗口的元素小于预期条件的元素时,我们将窗口的右指针向右移动,扩大窗口,但是对于上面的题目中,测试用例中存在负数的情况,那么对于窗口中出现了负数这种情况,我们是该将窗口右移直到满足条件还是将窗口左移直到剔除这个负数呢?这明显超出了我们之前规定的对于使用滑动窗口的条件,这也就是滑动窗口的局限性,而根据我们对于上文中对前缀和算法的描述,前缀和算法并不规定其中的元素一定要是正数或者负数,我们只是将其中的元素进行相加,而前缀和算法对于解决这种题目可谓大显身手。

除此之外,我们讲一下前缀和算法的应用相对于我们使用暴力解法具有怎样的优化:如果不创建一个新的数组来存储前n个元素的和,那么此时我们每次求和时,都需要从头到尾将元素和全部求一遍,对于一维数组,时间复杂度达到了O(n),但是如果我们将前面元素和进行存储,每次求和只需要调用我=我们前缀和数组中的元素,时间复杂度只有O(1),大大降低了时间复杂度;同理,对于二维数组而言,他可以将O(n^2)的时间复杂度降至O(1).

三.前缀和算法的模板代码

对于前缀和算法,一般存在一维的前缀和与二维的前缀和这样两种不同的算法模板,关于前缀和算法的模板,我们使用preSum来计算数组的前缀和,preSum[i]代表着前i个前i-1个数组元素的和;对于二维数组,preSum[i][j]则代表着行为 0到i-1,列为0-j-1元素的和

一维前缀和模板

如下是一维前缀和算法的模板代码:

    public int[] preSum(int[] nums) {
        int[] preSum = new int[nums.length+1];
        for (int i = 1; i < =nums.length; ++i) {
            preSum[i] = preSum[i - 1] + nums[i-1];
        }
        return preSum;
 
    }

二维前缀和模板

如下是二维前缀和的算法模板:对于二维的前缀和模板,是对一维前缀和模板的扩展,但在本质上是完全一致的,我们来描述有关二维数组的前缀和的模板:

我们给出这样一个图示:

对于二维数组的前缀和而言,表达式可以表述为这样:

每一个二维数组中的单位元素,相等于绿-澄-蓝+粉,我们同样给出一个二维数组,presum[nums.length+1][nums[0].length+1],presum[n][n]则代表0-n-1行,0-n-1列的数据总和 、

其模板表达式如下:

class NumMatrix {
    // 定义:preSum[i][j] 记录 matrix 中子矩阵 [0, 0, i-1, j-1] 的元素和
    private int[][] preSum;
    
    public NumMatrix(int[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        if (m == 0 || n == 0) return;
        // 构造前缀和矩阵
        preSum = new int[m + 1][n + 1];
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                // 计算每个矩阵 [0, 0, i, j] 的元素和
                preSum[i][j] = preSum[i-1][j] + preSum[i][j-1] + matrix[i - 1][j - 1] - preSum[i-1][j-1];
            }
        }
    }
    
    // 计算子矩阵 [x1, y1, x2, y2] 的元素和
    public int sumRegion(int x1, int y1, int x2, int y2) {
        // 目标矩阵之和由四个相邻矩阵运算获得
        return preSum[x2+1][y2+1] - preSum[x1][y2+1] - preSum[x2+1][y1] + preSum[x1][y1];
    }
}

除了完全的一维和二维的前缀和算法之外,前缀和算法的题目通常会与hash等数据结构组合出题

四.关于前缀和算法的经典题目练习

1.区域和检索 - 数组不可变力扣

力扣题目描述

解题思路

这是一个完全意义上的前缀和算法的题目,或者说这是一个书写前缀和算法模板的题目,对于这道题,我们直接使用我们前面使用的前缀和算法的模板即可:创建nums.length+1长度的presum[],对于presum[n],存储的是前n-1个元素的和,每次求和时直接使用presum中的元素即可。

实例代码

我们给出实例代码:
 

class Solution {
    public int subarraySum(int[] nums, int k) {
        //单纯使用前缀和,无法判别从哪里进行区分,这时我们需要将符合条件的数值直接存入map,再次需要时直接拿来使用即可
        int count=0;//记录最终的结果
        int pre=0;//记录前缀和
        //创建map
        HashMap<Integer,Integer>map=new HashMap<>();
        //如果是pre-target=0,代表从0开始刚好满足条件要求
        map.put(0,1);
        for(int i=0;i<nums.length;++i){
            pre+=nums[i];
            if(map.containsKey(pre-k)){
                count+=map.get(pre-k);
            }
            //将当前pre加入
            map.put(pre,map.getOrDefault(pre,0)+1);
        }
        return count;
    }
}
class NumArray {
    //本题使用前缀和算法,将使劲复杂度从O(n)降至O(1)
   private int[]preNums;

    public NumArray(int[] nums) {
        preNums=new int[nums.length+1];
        //循环时创建前缀和
        for(int i=1;i<preNums.length;++i){
            preNums[i]=preNums[i-1]+nums[i-1];
        }
        

    }
    
    public int sumRange(int left, int right) {
        return preNums[right+1]-preNums[left];
    }
}

2.和为 k 的子数组 力扣

题目描述

解题思路

这道题就是对我们一维数组前缀和算法的扩展了,它不仅仅使用简单的前缀和数组进行存储数组的前缀和,他同样需要使用hash表进行元素的记录和存储,这是一道前缀和数组和哈希表进行融合的题目,具体的解题思路如下:我们对于这道题目而言,难点在于对于简单的前缀和数组而言,我们无法判断从哪个地方进行断开(无法判断从那个地方作为起点,哪个地方作为终点是符合条件的),因为我们在此基础上使用哈希表,哈希表中存储的是当前元素为基准的前缀和与target之间的差距,如果差距为0(恰好满足)或者当前差距等于其前面元素的前缀和(如果前面元素的前缀和为4,此时前缀和-target=4 :前缀和比我们需要的和的值大4,刚好与前面的值进行抵消),这种也是符合条件的。

图示如下:

实例代码

我们给出实例代码

class Solution {
    public int subarraySum(int[] nums, int k) {
        //单纯使用前缀和,无法判别从哪里进行区分,这时我们需要将符合条件的数值直接存入map,再次需要时直接拿来使用即可
        int count=0;//记录最终的结果
        int pre=0;//记录前缀和
        //创建map
        HashMap<Integer,Integer>map=new HashMap<>();
        //如果是pre-target=0,代表从0开始刚好满足条件要求
        map.put(0,1);
        for(int i=0;i<nums.length;++i){
            pre+=nums[i];
            if(map.containsKey(pre-k)){
                count+=map.get(pre-k);
            }
            //将当前pre加入
            map.put(pre,map.getOrDefault(pre,0)+1);
        }
        return count;
    }
}

3.二维子矩阵的和力扣

题目描述

解题思路

这道题正是我们上面讲述的关于二维数组求前缀和的典型例题,我们在上面模板的基础上进行思路的讲解:对于红框内的元素和,等于绿框元素-蓝框-红框+黑框(其中蓝框和红框中也有黑框的部分,但是被颜色覆盖了),在此之前,我们首先要将元素进行初始化,初始化的过程相当于对元素进行分割的逆过程:单位元素加上两边的元素再减去重复的元素

实例代码

class NumMatrix {
    //创建二维的数组和
  private int[][]preNums;

    public NumMatrix(int[][] matrix) {
        preNums=new int[matrix.length+1][matrix[0].length+1];
        //创建并遍历
        for(int i=1;i<preNums.length;++i){
            for(int j=1;j<preNums[0].length;++j){
                preNums[i][j]=preNums[i][j-1]+preNums[i-1][j]-preNums[i-1][j-1]+matrix[i-1][j-1];
            }
        }
    }
    
    public int sumRegion(int row1, int col1, int row2, int col2) {
        return preNums[row2+1][col2+1]-preNums[row2+1][col1]-preNums[row1][col2+1]+preNums[row1][col1];
    }
}

4.除自身以外数组的乘积力扣

题目描述

解题思路

这道题目的解题思路相对而言比较巧妙,它将前缀和拓展为前缀积,而除了本身元素之外的其他元素的和,则恰好符合前缀积中premul[i]*lastmul[i]的定义,所以直接定义前缀后缀积进行元素的相乘即可

实例代码

class Solution {
    public int[] productExceptSelf(int[] nums) {
        //解题思路:通过前缀积和后缀积进行相乘,最终的结果就是除了nums[i]之外的相乘结果,需要注意的是:需要对前后缀积的含义进行一定的改变
        //定义前缀积
        int[]pre=new int[nums.length];
        //定义后缀积
        int[]last=new int[nums.length];
        //初始化并相乘
        pre[0]=1;
        for(int i=1;i<nums.length;++i){
            pre[i]=nums[i-1]*pre[i-1];
        }
        last[nums.length-1]=1;
        for(int i=nums.length-2;i>=0;--i){
            last[i]=last[i+1]*nums[i+1];
        }
        int[]res=new int[nums.length];
        //进行最后的结果返回
        for(int i=0;i<nums.length;++i){
            res[i]=pre[i]*last[i];
        }
        return res;
    }
}

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

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

相关文章

UCIe技术——概览索引

一、Chiplet技术概述 chiplet技术顺应了芯片生产与集成技术发展的趋势&#xff0c;也开拓了半导体技术发展的新的发展方向&#xff0c;将创造出一种新的芯片设计和商业模式 1.1 芯片生产与集成技术发展的趋势 &#xff08;1&#xff09;低半径高带宽的物理连线(bandwidth / …

css定位模式

1. 为什么需要定位&#xff1f; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"…

【python资料】pandas的条件查询

一、说明 在使用Pandas的DataFrame进行数据挖掘的时候&#xff0c;需要形形色色的条件查询&#xff0c;但是这些查询的基本语法是啥&#xff0c;查询的灵活性如何&#xff0c;本文将对他们进行详细列出&#xff0c;便于以后查阅。 二、Pandas条件查询方法 2.1 简单条件查询 1、…

单视觉L2市场「鲶鱼」来了,掀起数据反哺高阶新打法

作者 | 张祥威编辑 | 德新 智驾方案的降本行动仍在推进。 早年&#xff0c;单视觉L2市场的玩家以Mobileye、博世为主&#xff0c;后来国内智驾公司加入&#xff0c;共同推动 1V、1R1V、nR1V等不同的方案兴起&#xff0c;L2近乎成为车辆的必备功能。 当下&#xff0c;在行业降低…

SpringBoot启动扩展应用:干预优化+加快启动时间

目录 一、SpringBoot启动配置原理简述 二、SpringBoot启动过程干预 &#xff08;一&#xff09;ApplicationContextInitializer扩展 修改Spring Boot默认的environment属性 添加自定义的PropertySource 注册自定义bean &#xff08;二&#xff09;SpringApplicationRunL…

Vue绑定class样式与style样式

1&#xff0c;回顾HTML的class属性 答&#xff1a;任何一个HTML标签都能够具有class属性&#xff0c;这个属性可能只有一个值&#xff0c;如class"happs"&#xff0c;也有可能存在多个属性值&#xff0c;如class"happs good blue"&#xff0c;js的原生DOM针…

KDZK-F水轮发电机转子测试仪

一、产品概述 KDZK-F水轮发电机转子测试仪是判断发电机转子绕组有无匝间短路的专用仪器&#xff0c;可以自动、手动&#xff08;单向或双向&#xff09;测量转子绕组的电压、电流、阻抗、功率、相位角等参数。 二、功能与特点 旋转鼠标&#xff0c;操作更方便。 可选择快速的…

【014】C++数组之一维字符数组和二维字符数组

C数组之一维字符数组和二维字符数组 引言一、一维字符数组1.1、一维字符数组的初始化1.2、字符数组的遍历1.3、从键盘获取字符串1.4、使用示例 二、二维字符数组2.1、定义2.2、初始化2.3、访问 总结 引言 &#x1f4a1; 作者简介&#xff1a;专注于C/C高性能程序设计和开发&…

结构体 --- C语言

目录 1.结构体的声明 2.结构体变量的定义和初始化 3.结构体成员访问 4.结构体传参 1.结构体的声明 结构是一些值的集合&#xff0c;这些称为成员变量&#xff0c;结构的每个成员可以是不同类型的变量。 而数组是一组类型相同的元素的集合。 生活中的描述 人&#xff1a;名…

伪类元素的用法总结

1:自闭标签不适用伪类元素 自闭合标签 1. 一般标签   由于有开始符号和结束符号&#xff0c;因此可以在内部插入其他标签或文字。 <p>“绿叶&#xff0c;给你初恋般的感觉。”</p> 2. 自闭合标签   由于只有开始符号而没有结束符号&#xff0c;因此不可以在内…

亚马逊云科技宣布全面推出Amazon Aurora I/O-Optimized集群配置

自亚马逊云科技Amazon Aurora于2014年推出以来&#xff0c;成千上万的客户选择Aurora来运行其要求最严苛的应用程序。Aurora在全球范围内提供无与伦比的高性能和可用性&#xff0c;完全兼容MySQL和PostgreSQL&#xff0c;成本仅为商用数据库的十分之一。 许多亚马逊云科技客户受…

C# 队列(Queue)

目录 一、概述 二、基本的用法 1.添加元素 2.取出元素 1&#xff09;Dequeue 方法 2&#xff09;Peek 方法 3.判断元素是否存在 4.获取队列的长度 5.遍历队列 6.清空容器 7.Queue 泛型类 三、结束 一、概述 表示对象的先进先出集合。 队列和其他的数据结构一样&a…

微服务解码:揭示API的优势挑战与最佳实践

在当今快节奏的软件开发环境中&#xff0c;微服务已成为一种流行的架构模式。但微服务到底是什么&#xff1f;简而言之&#xff0c;微服务是一种将应用程序构建为松耦合、细粒度服务集合的方式&#xff0c;这些服务通过轻量级协议进行通信。这种架构风格使团队能够独立开发和部…

es Elasticsearch 六 java api spirngboot 集成es

目录 Java restApi Springboot 集成es 新增-同步 新增-异步 增删改查流程 _bulk 批量操作 Java restApi Springboot 集成es 新增-同步 Testpublic void te2() throws IOException {System.out.println(1);IndexRequest ir new IndexRequest("test");ir.id(&qu…

边缘计算AI硬件智能分析网关V1版的接入流程与使用步骤

我们的AI边缘计算网关硬件——智能分析网关目前有两个版本&#xff1a;V1版与V2版&#xff0c;两个版本都能实现对监控视频的智能识别和分析&#xff0c;支持抓拍、记录、告警等&#xff0c;在AI算法的种类上和视频接入上&#xff0c;两个版本存在些许的区别。V1的基础算法有人…

独立站怎么搭建?搭建一个独立站的10个建议和步骤

要搭建一个独立站&#xff08;也称为个人网站或博客&#xff09;&#xff0c;以下是一些建议和步骤&#xff1a; 选择一个合适的域名&#xff1a;选择一个简洁、易记且与您网站内容相关的域名。确保域名可用&#xff0c;并注册该域名。 寻找一个合适的主机服务提供商&#xff…

Nautilus Chain上线主网,为DeFi和流支付的未来构建基础

近日&#xff0c;加密行业权威平台 Coinmarketcap 发表了一篇名为“Zebec 模块化 Layer3 链 Nautilus Chain上线主网&#xff0c;为 DeFi 和流支付的未来构建基础”的文章&#xff0c;文中对 Zebec 生态公链 Nautilus Chain 的生态进展进行了简要的报道&#xff0c;并对其进行了…

服了呀,被现在的00后卷麻了....

现在的小年轻真的卷得过分了。前段时间我们公司来了个00年的&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。 最近和他聊了一次天&#xff0c;原来这位小老弟家里条…

SAP MM采购申请审批-成本中心

抬头审批的采购申请中行项目里的成本中心必须是同一个! 1、创建特性成本中心CT04 2、把特性分配给类CL02 3、维护分类审批策略 这些成本中心都可以使用&#xff0c;如果是单项就需要再CT04维护成多值。 如下采购申请&#xff0c;系统找不到审批策略, 2个行项目中&#xff0c;成…

复习之[ 查询帮助 ] 和 [ 输入输出管理 ]

1.查询命令用途--whatis # whatis 命令 : 查询命令的用法 -如果结果出现nothing , 有两种情况&#xff1a; &#xff08;1&#xff09;查询数据库没有更新&#xff0c;此时输入命令 mandb更新数据库即可。 &#xff08;2&#xff09;查询的命令不存在。 2.获得命令的简要帮…