数组前缀和算法技巧

news2024/9/21 12:45:04

一、什么是数组前缀和

数组中前缀和技巧(Prefix Sum Technique)是一种常见且有用的算法技巧,特别适用于需要频繁查询数组区间和的问题。这种技巧通过创建一个额外的数组来存储原始数组中特定位置之前所有元素的和,从而在需要计算任意子数组和时能够快速得出结果;

前缀和技巧的优点在于它可以将原先需要 O(n) 时间复杂度计算的区间和问题,转化为 O(1) 时间复杂度的查询操作。这在某些问题中可以大大提高算法的效率。

二、一维数组中的前缀和

LeetCode303题,区域和检索-数组不可变,计算数组区间内元素的和

给定一个整数数组  nums,处理以下类型的多个查询:

  1. 计算索引 left 和 right (包含 left 和 right)之间的 nums 元素的  ,其中 left <= right

实现 NumArray 类:

  • NumArray(int[] nums) 使用数组 nums 初始化对象
  • int sumRange(int i, int j) 返回数组 nums 中索引 left 和 right 之间的元素的 总和 ,包含 left 和 right 两点(也就是 nums[left] + nums[left + 1] + ... + nums[right] )

示例 1:

输入:
["NumArray", "sumRange", "sumRange", "sumRange"]
[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]]
输出:
[null, 1, -1, -3]

解释:
NumArray numArray = new NumArray([-2, 0, 3, -5, 2, -1]);
numArray.sumRange(0, 2); // return 1 ((-2) + 0 + 3)
numArray.sumRange(2, 5); // return -1 (3 + (-5) + 2 + (-1)) 
numArray.sumRange(0, 5); // return -3 ((-2) + 0 + 3 + (-5) + 2 + (-1))

 这道题暴力解法就是从left到right直接循环数组,累计和,时间复杂度是O(N),该题的最优解是使用前缀和技巧,核心思路是定义一个新的数组preSum,preSum[i]代表nums[0...i-1]的累加和,如图所示:

如果我想求索引区间 [1, 4] 内的所有元素之和,就可以通过 preSum[5] -preSum[1] 得出。sumRange函数仅仅需要做一次减法运算,就可以算出left和right之间的元素和,时间复杂度是O(1)

具体实现代码如下:

public class NumArray {
    /**
     * 前缀和数组,pre[i]代表0到i-1的和
     */
    private int[] preSum;

    public NumArray(int[] numArray) {
        //初始化前缀和
        if(numArray!=null&&numArray.length>0){
           int len = numArray.length;
           preSum = new int[len+1];
            //计算0到i-1的累加和
            for (int i = 1; i <= len; i++) {
                preSum[i] = preSum[i-1]+numArray[i-1];
            }
        }
    }

    /**
     * 计算left 到right之间的累计和
     * @param left
     * @param right
     * @return
     */
    public int sumRange(int left, int right){
        return preSum[right+1]-preSum[left];
    }

    public static void main(String[] args) {
        int[] array = {-2, 0, 3, -5, 2, -1};
        NumArray numArray = new NumArray(array);
        int ret = numArray.sumRange(0, 5);
        System.out.println(ret);
    }

三、二维矩阵中的前缀和

leetcode 304题二维区域和检索 - 矩阵不可变,计算矩形范围内元素的总和,题目描述如下:

给定一个二维矩阵 matrix,以下类型的多个请求:

  • 计算其子矩形范围内元素的总和,该子矩阵的 左上角 为 (row1, col1) ,右下角 为 (row2, col2) 。

实现 NumMatrix 类:

  • NumMatrix(int[][] matrix) 给定整数矩阵 matrix 进行初始化
  • int sumRegion(int row1, int col1, int row2, int col2) 返回 左上角 (row1, col1) 、右下角 (row2, col2) 所描述的子矩阵的元素 总和 。

输入: 
["NumMatrix","sumRegion","sumRegion","sumRegion"]
[[[[3,0,1,4,2],[5,6,3,2,1],[1,2,0,1,5],[4,1,0,1,7],[1,0,3,0,5]]],[2,1,4,3],[1,1,2,2],[1,2,2,4]]
输出: 
[null, 8, 11, 12]

解释:
NumMatrix numMatrix = new NumMatrix([[3,0,1,4,2],[5,6,3,2,1],[1,2,0,1,5],[4,1,0,1,7],[1,0,3,0,5]]);
numMatrix.sumRegion(2, 1, 4, 3); // return 8 (红色矩形框的元素总和)
numMatrix.sumRegion(1, 1, 2, 2); // return 11 (绿色矩形框的元素总和)
numMatrix.sumRegion(1, 2, 2, 4); // return 12 (蓝色矩形框的元素总和)

解题思路:

1、理解题意,矩形的坐标,也是从左上角(0,0)开始的,代表的元素是3,是一个小矩形 ,根据题意,(0,0)左上角到右下角元素和是3,其他任意矩形都是从左上角延伸过来的,计算矩形坐标位置时,不能看点,要看一个面,注意计算的是起点矩形的左上角与终点矩形的右下角所包围的元素数据的总和

2、任意子矩阵元素的和可以转换成周边几个大矩阵的元素和的运算

3、利用数组前缀和思想,第一步如何初始化二维数组前缀和

定义一个二维前缀和数组preSum,preSum[i][j]代表 [0,0]到[i-1,j-1]元素的总和,根据上图计算逻辑

preSum[i,j] = preSum[i-1][j]+preSum[i][j-1]-preSum[i-1][j-1]+matrix[i-1][j-1]

4、如何计算任意子矩阵元素的和

 

 假设左上角是[row1,col1],右下角[row2,col2],由上图可知,子矩阵的元素总和计算公式如下

子矩阵元素总和=preSum[row2+1][col2+1] - preSum[row1][col2+1] - preSum[row2+1][col1]+preSum[row1][col1]

核心实现代码如下:

public class NumMatrix {
    /**
     * 定义preSum[i][j]记录matrix中子矩阵[0,0,i-1,j-1]的元素和
     */
    private int[][] preSum;

    public NumMatrix(int[][] matrix) {
        if (matrix == null) {
            return;
        }
        int row = matrix.length;
        int col = matrix[0].length;
        if (col == 0) {
            return;
        }
        //构造前缀和
        preSum = new int[row + 1][col + 1];
        for (int i = 1; i <= row; i++) {
            for (int j = 1; j <= col; j++) {
                preSum[i][j] = preSum[i][j - 1] + preSum[i - 1][j] - preSum[i - 1][j - 1] + matrix[i - 1][j - 1];
            }
        }

    }

    /**
     * 计算子矩阵【r1,c2,r2,c2]的元素和
     *
     * @param r1
     * @param c1
     * @param r2
     * @param c2
     * @return
     */
    public int sumRange(int r1, int c1, int r2, int c2) {
        //目标矩阵之后由四个相邻矩阵运算获得
        return preSum[r2 + 1][c2 + 1] - preSum[r1][c2 + 1] - preSum[r2 + 1][c1] + preSum[r1][c1];
    }

    public static void main(String[] args) {
        int[][] matrix = {{3, 0, 1, 4, 2}, {5, 6, 3, 2, 1}, {1, 2, 0, 1, 5}, {4, 1, 0, 1, 7}, {1, 0, 3, 0, 5}};
        NumMatrix numMatrix = new NumMatrix(matrix);
        int ret = numMatrix.sumRange(1, 2, 2, 4);
        System.out.println(ret);

    }

 以上算法的核心思想都是前缀和的思想,重点就是空间换时间,将sumRange函数的时间复杂度优化到O(1),

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

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

相关文章

【图论】并查集(Union-find Sets)

文章目录 前言一、并查集(Union-find Sets)基本概念基本操作步骤 二、并查集的操作步骤1. 初始化 init2. 查询 find、合并 union&#xff08;未进行路径压缩&#xff09;3. 查询 find、合并 union&#xff08;路径压缩&#xff09; 三、Kruskal 算法中 环 的判断并查集的使用 总…

C++中的string介绍(常用函数)

string类 为什么学习string类C语言中的字符串 标准库中的string类string类(了解)auto和范围forauto关键字范围for string类的常用接口说明(注意下面只讲解最常用的接口)string类对象的常见构造 string类对象的容量操作string类对象的访问及遍历操作string类对象的修改操作strin…

洛谷 P6280 [USACO20OPEN] Exercise G

题目来源于&#xff1a;洛谷 题目本质&#xff1a;dp&#xff0c;素数筛法&#xff0c;质数 本题与P4161基本一模一样 首先&#xff0c;分析题目发现&#xff0c;某个排列的需要进行恰好 K 步变回原样&#xff0c;这个时候K的值就是这个排序中各个环的长的的最小公倍数(lcm)。…

wechatAssetsPicker组件的用法

文章目录 1. 概念介绍2. 思路与方法2.1 使用思路2.2 使用方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"ImagePicker使用总结"相关的内容&#xff0c;本章回中将介绍wechat_assets_picker这个三方包.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介…

红外遥控设计验证

前言 红外遥控是一种无线、非接触控制技术&#xff0c;具有抗干扰能力强&#xff0c;信息传输可靠&#xff0c;功耗低&#xff0c;成本低&#xff0c;易实现等显著优点&#xff0c;被诸多电子设备特别是家用电器广泛采用&#xff0c;并越来越多的应用到计算机和手机系统中。本文…

位运算(1)

1.获取第i位的二进制数(只出现一次数字2)&#xff1a; 2.将第i位的二进制设为1&#xff08;只出现一次数字2&#xff09;&#xff1a; 3.int最低位为1的数&#xff08;只出现一次数字3&#xff09;&#xff1a;

【2024】docker镜像拉取失败网络超时解决办法-自建镜像加速服务

目录 前言一、直接配置镜像加速地址二、自己搭建中转服务进行镜像加速1、Fork副本2、创建cloudflare3、注册域名4、测试使用5、配置变量 前言 近期docker官方镜像拉取经常容易出现网络超时&#xff0c;下面为一些常用的处理解决部分 实现docker镜像拉取加速解决方案 直接使用一…

【iOS】Block底层分析

目录 前言Block底层结构Block捕获变量原理捕获局部变量&#xff08;auto、static&#xff09;全局变量捕获实例self Block类型Block的copyBlock作为返回值将Block赋值给__strong指针Block作为Cocoa API中方法名含有usingBlock的方法参数Block作为GCD API的方法参数Block属性的写…

[第五空间 2021]EasyCleanup

题目源代码&#xff1a; <?php if(!isset($_GET[mode])){ highlight_file(__file__); }else if($_GET[mode] "eval"){ $shell isset($_GET[shell]) ? $_GET[shell] : phpinfo();; if(strlen($shell) > 15 | filter($shell) | checkNums($shell)) exit(&q…

git学习使用碰到的问题1

本来在B站上看到的关于stash的使用时视频末尾讲到git stash drop 编号 会删除暂存记录 确实也是这么回事&#xff0c;但是末尾说到git stash pop 编号时up主说在恢复工作进度的时候我们可以直接删除掉这个工作记录可以直接使用 git stash pop stash{0} 使用完以后却出现了如上图…

AI项目二十四:yolov10竹签模型,自动数竹签

若该文为原创文章&#xff0c;转载请注明原文出处。 原本是为部署RK3568而先熟悉yolov10流程的&#xff0c;采用自己的数据集&#xff0c;网上很多&#xff0c;检测竹签&#xff0c;并计数。 1、环境搭建 1.1 官方下载源码 官网地址&#xff1a;YOLOv10 gitbub官网源码 利用…

各类函数调用

目录 getpwuid函数 查看uid的name​编辑 symlink函数软链接&#xff08;创建快捷方式&#xff09; remove函数 rename函数 link硬链接 truncate函数控制文件大小 perror报错函数 strerror报错函数序列表 error报错函数&#xff1a;详细报错 Makefile编译函数、工程管…

考试题型宏观分析之公共营养师三级

背景 第一遍知识学习之后&#xff0c;打印《2023.10.14公共营养师三级真题》进行第一次摸底&#xff0c;首要目标在于通过摸底&#xff0c;对于考试题型进行宏观分析和了解&#xff0c;其次&#xff0c;对于后续的学习进行有的放矢 直至2024-08-18&#xff0c;对于上述资料的一…

ubuntu配pip的源

临时使用源 pip install [包名] -i [pip源URL]# 示例 pip install pytest -i https://pypi.tuna.tsinghua.edu.cn/simple更换配置pip镜像源 step1&#xff1a;创建一个配置文件 mkdir ~/.pip/ cd .pip sudo vim pip.conf step2:填写源信息&#xff0c;保存并退出【:wq】 [g…

Android 架构模式之 MVC

目录 架构设计的目的对 MVC 的理解Android 中 MVC 的问题试吃个小李子ViewModelController 大家好&#xff01; 作为 Android 程序猿&#xff0c;MVC 应该是我们第一个接触的架构吧&#xff0c;从开始接触 Android 那一刻起&#xff0c;我们就开始接触它&#xff0c;可还记得我…

【秋招笔试】8.18科大讯飞秋招-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收…

【接口测试】Postman + newman超详细图文安装教程

一、Postman安装 下载网址&#xff1a;Postman API Platform 打开网址&#xff0c;选择自己系统对应的版本进行下载。 双击Postman安装包&#xff0c;全自动安装&#xff0c;不需要任何人为干预。安装完成后&#xff0c;页面如下图&#xff0c;点击手动打开注册页面。 自行…

超详细!!!electron-vite-vue开发桌面应用之引入UI组件库element-plus(四)

云风网 云风笔记 云风知识库 一、安装element-plus以及图标库依赖 npm install element-plus --save npm install element-plus/icons-vue npm i -D unplugin-icons二、vite按需引入插件 npm install -D unplugin-vue-components unplugin-auto-importunplugin-vue-componen…

Linux-DNS域名解析服务

系列文章目录 提示&#xff1a;仅用于个人学习&#xff0c;进行查漏补缺使用。 1.Linux网络设置 2.LinuxDHCP服务 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言提示&#xff1a;以下是本篇文章…

扫描切除-实体轮廓:方程式驱动曲线路径vs螺旋线路径

最近,在使用solidworks2018的过程中,接触到扫描切除-实体轮廓命令,如图1-2所示。此命令可以使用一个实体来切除另一个实体,用来切除的实体可以按一定的轨迹运动。测试过程中发现,这个命令频繁出错,切除失败,体验实在是太差了。下面对比了在该命令下使用方程式驱动曲线和…