【Java算法专场】前缀和(上)

news2024/11/15 5:41:02

前言

  在求数组或者矩阵求和等问题,我们如果采用暴力解法,时间复杂度可能会达到O(n)或者更高,因此,我们可利用前缀和来解决。

前缀和

前缀和是指序列中的n项和,相当于数学问题中秋数列的前n项和。主要用于数组或列表中求解子数组的和、查询区间和等问题,能够将时间复杂度从O(n)降低到O(1)

接下来我们就通过题目来更好的来理解前缀和。

【模板】前缀和

算法分析

本道题就是在长度为n的数组中根据输入的区间下标[L,R]来计算区间和。如果我们使用暴力解法,时间复杂度为O(n),但是我们有更优的解法,使用前缀和,能使查询区间和的复杂度降为O(1).

算法步骤

  1. 初始化:根据题目需求,定义变量n,q,l,r,并等待输入。定义数组a长度为(n+1),同理,这里需要借助一个前缀和数组dp,长度也为(n+1);
  2. 预处理:给n,q,数组元素,l,r赋值之后,就可以进行下步操作。
  3. 处理前缀和数组:前缀和数组顾名思义就是用来计算前缀和的大小,(前缀和数组中dp[i]指的是包括i在内之前的元素之和),如果我们每次计算dp[i]都要遍历到i,时间复杂度为O(N^2),但我们可以知道的是,每次计算前缀和,都是dp[i-1]+a[i](即i之前的前缀和+a[i]).所以我们计算前缀和dp[i]=dp[i-1]+a[i]
  4. 计算区间和:当我们处理完前缀和数组之后,计算[L,R] 的区间和,我们可以理解为计算(R)之前的和减去(L-1)之前的和,即sum=dp[r]-dp[l-1]
  5. 处理边界:我们这里计算前缀和时,数组下标应该从1开始,若从0开始,当计算前缀和数组dp[0]=dp[-1]+a[0],就会越界,因此下标应该从1开始。且a[0]=dp[0]=0.
  6. 变量类型:通过题目要求,0<=a[i]<=10^9,因此我们不能使用int类型,而是使用long

算法代码

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int q = in.nextInt();
        long[] a = new long[n + 1];
        long[] dp = new long[n + 1];
        a[0] = dp[0] = 0;
        for (int i = 1; i <= n; i++) {
            a[i] = in.nextLong();
        }
        //处理前缀和数组
        for (int i = 1; i <= n; i++) {
            dp[i] = dp[i - 1] + a[i];
        }
        //计算区间和
        while (q > 0) {
            int l = in.nextInt();
            int r = in.nextInt();
            long sum = dp[r] - dp[l - 1];
            System.out.println(sum);
            q--;
        }
    }
}

时间复杂度为O(n+q),n是数组长度,q是查询次数。

空间复杂度为为O(n),需要借助dp数组。

算法示例

8 1
3 4 5 8 2 3 4 5
4 7

为例 

第一步:初始化 、并预处理a数组

根据输入的值:n=8,q=1 a[9]={0,3,4,5,8,2,3,4,5}

第二步:处理前缀和数组

dp[9]={0,3,7,12,20,22,25,29,35}

第三步:根据L和R计算前缀和 

在输入中,我们可以得到L=4,R=7

所以前缀和sum=dp[7]-d[3]=29-12=17

我们可以测试一下,可以看到,结果确实与我们算的一致。

【模板】

根据这道题目,我们可以总结出计算前缀和时的模板:

 【模板】二维前缀和

 算法分析

本道题是要在一个矩阵中求子矩阵的和,如果我们使用暴力解法,时间复杂度为O(n*m*q),会超时,但对于这种求子矩阵和的问题,我们可以使用前缀和来解决。

在说算法步骤之前,先来补充个知识,我们在计算一个矩阵的大小时,其实是可以将分为几部分来求解的,对于本道题的矩阵,我们可以分解为:

算法步骤

  1. 初始化:定义n和m,并创建(n+1)*(m+1)的二维数组a和(n+1)*(m+1)的二维前缀和数组dp。这里是为了防止溢出。同时也需要创建一个(n+1)*(m+1)的前缀和矩阵。
  2. 矩阵处理:在上面的图片中,我们知道了一个矩阵可以切块计算。那么我们在计算dp[i][j]的时候,其实也是可以分成4部分。我们在计算A部分的时候,是很容易计算的(其实就是dp[i-1][j-1]的和),但我们想要知道B、C部分的和,那是有点困难的,但我们可以看出,如果我们把A和B、A和C结合来,其实就是前缀和dp[i-1][j]和dp[i][j-1],这从我们前缀和数组中是可以知道的。所以,我们在处理前缀和矩阵的时候,可以根据公式:

dp[i][j]=dp[i-1][j]+dp[i][j-1]+arr[i][j]-dp[i-1][j-1]

即S=(A+B)+(A+C)+D-A

3.计算子矩阵和:当我们处理完前缀和矩阵之后,就可以利用矩阵来进行求和。

根据所给的坐标(X1,Y1),(X2,Y2),我们可以得到以下:

同理的,我们根据坐标,将矩阵分为4部分,其中D矩阵就是我们所要求的子矩阵。我们想要求D的话,根据

D=(A+B+C+D)-(A+B)-(A+C)+A

  =dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1]

即可得到。

算法代码 

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
        int q=sc.nextInt();
        long[][] a=new long[n+1][m+1];
        long[][] dp=new long[n+1][m+1];
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                a[i][j]=sc.nextLong();
            }
        }
        //预处理矩阵
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                //S=(A+B)+(A+C)+D-A
                dp[i][j]=dp[i-1][j]+dp[i][j-1]+a[i][j]-dp[i-1][j-1];
            }
        }

        //求子矩阵和
        while(q>0){
            int x1=sc.nextInt();
            int y1=sc.nextInt();
            int x2=sc.nextInt();
            int y2=sc.nextInt();
            //sum=(A+B+C+D)-(A+B)-(A+C)+A=D
            long sum=dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1];
            System.out.println(sum);
            q--;
        }
    }
}

时间复杂度为O(n*m)+O(q),n,m为二维数组的行,列,q为查询次数。

空间复杂度为O(n*m).需要开辟两个数组,a和dp。 

算法示例

3 4 3
1 2 3 4
3 2 1 0
1 5 7 8
1 1 2 2

为例

第一步:初始化

根据输入的值,可以得到:

n=3,m=4,q=3  

a数组如下 

第二步:处理前缀和矩阵

根据二维数组a,我们可以计算出前缀和数组dp为

【模板】

寻找数组的中心下标

算法分析 

这道题是要我们在数组中,找以一个点为中心,左右区间和相等。这里我们可以利用前缀和来进行解答。这里我们需要利用到两个数组,yp和np。

算法步骤

  1. 初始化:定义n,并初始化为nums.length.定义两个二维数组yp(前缀合)和np(后缀和)并设置长度为n;yp用来存储从前往后的和,np用来存储从后往前的和。
  2. 预处理yp和np:yp从1开始往后遍历,yp=yp[i-1]+nums[i-1],循环条件为:i<n;np从nums.length-2开始,np[i]=np[i+1]+nums[i+1],循环条件为:i>=0.
  3. 判断:当处理完yp和np之后,我们就可判断yp和np中数组元素是否相等,若相等,证明以该点为中心点的左右区间和相等,返回此时的下标即可。当遍历完数组,若没有相等的,则返回-1.

算法代码

    /**
     * 寻找数组的中心索引
     * 中心索引的左侧元素之和等于右侧元素之和
     * 
     * @param nums 输入的整数数组
     * @return 返回中心索引,如果不存在,则返回-1
     */
    public int pivotIndex(int[] nums) {
        // 数组长度
        int n=nums.length;
        // yp数组用于存储从左侧累加的元素之和
        int[] yp=new int[n];
        // np数组用于存储从右侧累加的元素之和
        int[] np=new int[n];
        
        // 从左侧开始累加,计算每个位置左侧元素之和
        for(int i=1;i<n;i++){
            yp[i]=yp[i-1]+nums[i-1];
        }
        
        // 从右侧开始累加,计算每个位置右侧元素之和
        for(int i=n-2;i>=0;i--){
            np[i]=np[i+1]+nums[i+1];
        }
        
        // 遍历数组,寻找中心索引
        for(int i=0;i<n;i++){
            // 当找到左右两侧元素之和相等的位置时,返回该索引
            if(yp[i]==np[i]){
                return i;
            }
        }
        // 如果没有找到中心索引,返回-1
        return -1;
    }

算法示例:

以nums = [1, 7, 3, 6, 5, 6]为例

第一步:初始化

n=6

第二步:预处理

yp={0,1,8,11,16,21}

np={27,20,17,11,6,0}

第三步:判断

我们可以看到,yp[3]=np[3]=11,此时返回下标3即可。

除自身以外数组的乘积

 算法分析

本道题与上一道题类似,不过这道题是为了求除当前下标的数,其他数的乘积。那么这道题我们照样可以用前缀和。这里同样需要两个数组,前缀和数组yp,后缀和数组np。

算法步骤

  1. 初始化:定义n,并初始化为nums.length,yp和np数组的长度设置为n。
  2. 预处理数组:与上道题循环条件,起始位置一致。yp[i]=yp[i-1]+nums[i-1]. np[i]=np[i+1]+nums[i+1].(需要注意的是这里yp[0]和np[n-1]都需要初始化为1,否则相乘时会导致数据出错)
  3. 数组相乘:这里需要设置数组ans并设置长度为n,用来存储前缀和和后缀和的乘积。
  4. 返回结果:当前缀和和后缀合相乘完并存储在ans后,返回ans即可。

算法代码

/**
 * 计算数组中每个元素的乘积,排除自身
 * 这个方法通过使用两个辅助数组来优化计算,避免了对每个元素重复遍历整个数组求乘积的过程
 * 
 * @param nums 原始整数数组
 * @return 包含每个元素对应位置上的乘积结果的数组
 */
public int[] productExceptSelf(int[] nums) {
    // 数组长度
    int n = nums.length;
    // yp数组用于存储每个位置左侧所有数字的乘积
    int[] yp = new int[n];
    // np数组用于存储每个位置右侧所有数字的乘积
    int[] np = new int[n];
    
    // 初始化左侧乘积数组,yp[0]为1,因为第一个元素左侧没有元素,乘积为1
    yp[0] = 1;
    for (int i = 1; i < n; i++) {
        yp[i] = yp[i - 1] * nums[i - 1];
    }
    
    // 初始化右侧乘积数组,np[n-1]为1,因为最后一个元素右侧没有元素,乘积为1
    np[n - 1] = 1;
    for (int i = n - 2; i >= 0; i--) {
        np[i] = np[i + 1] * nums[i + 1];
    }
    
    // 构建答案数组,每个元素是对应位置的左侧乘积和右侧乘积的乘积
    int[] ans = new int[n];
    for (int i = 0; i < n; i++) {
        ans[i] = yp[i] * np[i];
    }
    
    return ans;
}

时间复杂度为O(n),n为数组长度。

空间复杂度为O(n)

算法示例

以nums = [1,2,3,4]为例

第一步:初始化

n=4

第二步:处理缀合数组

根据yp[i]=yp[i-1]+nums[i-1]可得

yp={1,1,2,6}

根据np[i]=np[i+1]+nums[i+1]可得

np={24,12,4,1}

第三步:缀合数组相乘

ans={24,12,8,6}

第四步:返回结果

此时将ans数组返回即可。


前缀和上篇就先到这里了~

若有不足,欢迎指正~ 

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

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

相关文章

[Bugku] web-CTF-POST

1.开启环境 2.根据题目得知使用POST传参&#xff0c;即可得到flag

[240802] 有关 Homebrew 的安全审核 | Running C++ anywhere like a script

目录 有关 Homebrew 的安全审核Running C anywhere like a script 有关 Homebrew 的安全审核 Trail of Bits 对 macOS 包管理工具 Homebrew 进行了安全审计&#xff0c;重点关注其核心代码库和 CI/CD 流程。审计发现了 Homebrew 中存在的一些问题&#xff0c;这些问题可能被攻…

来点八股文(四) 数据检索ESOLAPCK和Calcite

文章目录 压缩存储&索引codegen优化器向量化 如何解决深度分页问题&#xff1f; scoll 游标分页 使用的是快照&#xff0c;没法保证实时fromsize 最差的实现&#xff0c;内存占用和时间都很差search_after 搜索上下文 通过记录自增id来查询&#xff0c;减少了内存占用 luc…

GDAL——地理空间数据抽象库在VS2022下的编译

上图是GDAL在github上的首页截图&#xff0c;GDAL库本身是没法运行的(一开始想运行它看看&#xff0c;但后知后觉一个库单独怎么能运行呢哈哈哈哈哈哈)。编译成功后&#xff0c;会生成几个文件夹&#xff0c;里面有一些文件&#xff0c;把这些文件配置到合适的位置后&#xff0…

css-grid布局之美

一&#xff0c;grid布局概述 网格布局&#xff08;Grid&#xff09;是最强大的 CSS 布局方案。 它将网页划分成一个个网格&#xff0c;可以任意组合不同的网格&#xff0c;做出各种各样的布局。以前&#xff0c;只能通过复杂的 CSS 框架达到的效果&#xff0c;现在浏览器内置…

游戏加速器推荐 网游加速器排行榜

游戏加速器推荐&#xff0c;玩游戏用什么加速器&#xff01;我得给你推荐一款我常用的。首先呢&#xff0c;就是深度加速器&#xff0c;它针对目前手游网游的游戏加速效果特别棒&#xff0c;而且界面也很友好。 另外&#xff0c;还有深度加速器&#xff0c;这款加速器不仅支持国…

使用模版完成不同数据类型的数组的选择排序

目录 6.模版(167-263) 6.1函数模板 6.1.1函数模版注意事项 6.1.2函数模版案例--选择排序 1. 比较排序的基本概念 2. 决策树 3. 决策树的深度 4. 结论 5.选择排序示例: 6.模版(167-263) (项目先跳过) 模板不能直接使用,它只是一个框架. 模板不是万能的. 6.1函数模板…

HCIE还是CCIE?高级认证到底要怎么选?

HCIE与CCIE&#xff0c;作为网络技术领域的两大旗舰认证&#xff0c;一直是IT专业人士追求的目标。 它们不仅代表了个人技术能力的权威认可&#xff0c;更是职业生涯中的重要里程碑。 然而&#xff0c;面对这两个同为高级认证的金字招牌&#xff0c;许多人不禁要问&#xff1a;…

基于FPGA的数字信号处理(21)--超前进位加法器

目录 1、什么是超前进位加法器 2、CLA加法器的关键路径 3、CLA加法器的Verilog实现 4、CLA加法器的时序性能 5、总结 文章总目录点这里&#xff1a;《基于FPGA的数字信号处理》专栏的导航与说明 1、什么是超前进位加法器 在之前的文章&#xff0c;我们介绍了行波进位加法器…

安装linux系统的时候没有允许root用户远程登录,怎么修改?

1、进入/etc/ssh/sshd_config vim /etc/ssh/sshd_config /etc/ssh/sshd_config通常是 SSH 服务&#xff08;Secure Shell&#xff09;的配置文件。 SSH 是一种用于安全远程登录和执行命令的网络协议。在这个配置文件中&#xff0c;您可以设置诸如端口号、允许或拒绝的登录用户…

数据结构(5.4_1)——树的存储结构

树的逻辑结构 双亲表示法(顺序存储) 每个结点中保存指向双亲的“指针” #define MAX_TREE_SIZE 100//树中最多结点typedef struct {//树的结点定义int data;//数据元素int parent;//双亲位置域 }PTNode; typedef struct {//树的类型定义PTNode nodes[MAX_TREE_SIZE];//双亲表…

红外热成像手持终端:从建筑检测到野外搜救的全方位应用

红外热成像手持终端&#xff0c;凭借其独特的红外探测与夜视功能&#xff0c;广泛应用于多个关键领域。无论是军事侦察、消防救援中的夜间作业&#xff0c;还是电力巡检、野生动物观察等多样场景&#xff0c;其精准的红外热成像技术均能提供至关重要的实时数据&#xff0c;助力…

TrainingArguments 的ignore_data_skip解释

文章目录 0. 背景1. 官方解释2. 查看源码3. 验证4. 总结 0. 背景 在采用 HuggingFace 提供的 Transformers 库来训练模型时&#xff0c;如果出现模型训练中断的情况&#xff0c;此时我们希望断点接训&#xff0c;TraningArguments 有一个参数&#xff1a;resume_from_checkpoi…

都从哪里下载量产工具,我给大家推荐一下吧

就推荐量产部落&#xff01;因为从事固态硬盘维修这行&#xff0c;而且我自己也喜欢DIY&#xff0c;所以我比较关注量产工具下载。 要说量产工具&#xff0c;就得从U盘和固态硬盘的兴衰开始说起&#xff0c;从2016年开始&#xff0c;U盘就在走下坡路了&#xff0c;U盘量产工具…

英特尔裁员、暂停分红和市场挑战

英特尔&#xff08;INTC&#xff09;近日宣布了一系列战略调整&#xff0c;以应对其面临的严峻挑战。这家总部位于加利福尼亚州圣克拉拉的芯片制造商计划裁员超过15%&#xff0c;并从第四季度起暂停派息&#xff0c;以重振其盈利能力。 股价暴跌与市值蒸发 英特尔的这一决定导…

找不到的软件资源,试试这个网站

0daydown是一个提供多种资源下载的网站&#xff0c;包括软件、电影、音乐和游戏等。该网站通常会转载其他0day站点或PT站点的内容&#xff0c;并以其丰富的资源和便捷的下载方式而闻名。此外&#xff0c;0daydown还收录了稀缺的0day软件&#xff0c;并提供百度网盘下载链接。 …

双 Token 三验证解决方案

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 问题分析 以往的项目大部分解决方案为单 token&#xff1a; 用户登录后&#xff0c;服务端颁发 jwt 令牌作为 token 返回每次请求&#xff0c;前端携带 token 访问&#xff0c;服务端解析 token 进行校验和…

serial---- vulnhub打靶

1.新建虚拟机&#xff0c;虚拟硬盘使用vulnhub下载提供的虚拟硬盘文件 2.打开虚拟机&#xff0c;扫描网段&#xff0c;确定IP(或者arp -a) 3.发现没有robots.txt&#xff0c;以及一些常见admin,www.zip目录文件&#xff0c;尝试扫目录 拿另一个工具扫一下看看多了一个 4.发现备…

凸优化学习之旅

目录标题 专业名词MM算法CCP算法&#xff1a;代码说明 SCA算法&#xff1a;连续松弛梯度投影算法 分支定界搜索法凸问题辨别OA算法λ-representationADMM算法代码说明 BCD算法BCD&#xff08;Block Coordinate Descent&#xff09;代码示例与ADMM的区别总结 2024年5月6日15:15:…

2024 年 5 款顶级的免费和付费 PDF 编辑器个人评测

PDF 为企业、学校或一般用途提供了一种共享各种信息的便捷方式。您可以在笔记本电脑和智能手机上轻松查看 PDF 文档。但大多数图片查看器和 PDF 阅读器不允许您编辑 PDF。因此&#xff0c;当您想要修改 PDF 文件中的图像或文本时&#xff0c;您需要一个PDF 编辑器。 似乎没有太…