前缀和算法

前缀和可以快速求出数组中某个连续区间的和

  1. 预处理出来一个前缀和数组

为了处理边界情况,下标从1开始

dp[i]表示原数组[ 1 , i ]区间内所有元素之和
dp[i]=dp[i-1]+原数组[i]

  1. 使用前缀和数组

文章目录

  • 【模板】前缀和
  • 【模板】二维前缀和
  • 寻找数组的中心下标
  • 除自身以外数组的乘积
  • 和为k的子数组
  • 和可被K整除的子数组
  • 连续数组
  • 矩阵区域和

【模板】前缀和

算法思路:

  1. 先预处理出来⼀个「前缀和」数组:

设前缀和数组为dp
dp[i] 表示: [1, i] 区间内所有元素的和,那么 dp[i - 1] 里面存的就是 [1, i - 1] 区间内所有元素的和,那么可得递推公式: dp[i] = dp[i - 1] + arr[i] ;

  1. 使用前缀和数组,「快速」求出「某⼀个区间内」所有元素的和:

当询问的区间是 [l, r] 时:区间内所有元素的和为: dp[r] - dp[ l - 1] 。

import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);//1.读入数据int n = in.nextInt(), q = in.nextInt();int[] arr = new int[n+1];for(int i = 1; i <= n; i++) arr[i] = in.nextInt();//2.预处理一个前缀和数组long[] dp = new long[n+1];for(int i = 1; i <= n; i++) dp[i] = dp[i-1] + arr[i];//3.使用前缀和数组while(q > 0) {int l = in.nextInt(), r = in.nextInt();System.out.println(dp[r]-dp[l-1]);q--;}}
}

【模板】二维前缀和

算法思路:
类比于⼀维数组,我们处理出来从 [0, 0] 位置到 [i, j] 位置这片区域内所有元素的累加和。

  1. 预处理出来⼀个前缀和矩阵

下标直接从 1 开始

  1. 前缀和矩阵中 dp[i][j] 的含义,以及如何递推二维前缀和方程

dp[i][j] 表示,从 [1, 1] 位置到 [i, j] 位置这段区域内,所有元素的累加和
递推方程:
我们可以将 [1, 1] 位置到 [i, j]位置这段区域分解成下面的部分:
在这里插入图片描述

整个面积 = A+B+C+D = (A+B) + (A+C) + D - A = dp[i-1][j] + dp[i][j-1] + arr[i][j] - dp[i-1][j-1]
接下来分析如何使用这个前缀和矩阵
我们要求的就是A的面积:
A = 整个面积 - (C + D)- (B + D)+ D = 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 in = new Scanner(System.in);//1.读入数据int n = in.nextInt(), m = in.nextInt(), q = in.nextInt();int[][] arr = new int[n+1][m+1];for(int i = 1; i <= n; i++) {for(int j = 1; j <= m; j++) {arr[i][j] = in.nextInt();}}//2.预处理一个前缀和矩阵long[][] dp = new long[n+1][m+1];for(int i = 1; i <= n; i++) {for(int j = 1; j <= m; j++) {dp[i][j] = dp[i-1][j]+dp[i][j-1]+arr[i][j]-dp[i-1][j-1];}}//3.使用前缀和矩阵while(q > 0) {int x1 = in.nextInt(), y1 = in.nextInt(), x2 = in.nextInt(), y2 = in.nextInt();System.out.println(dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1]);q--;}}
}

寻找数组的中心下标

算法思路:
由中心下标的定义可知,除中心下标的元素外,该元素左边的「前缀和」等于该元素右边的「后缀和」。

  • 因此,我们可以先预处理出来两个数组,⼀个表示前缀和,另⼀个表示后缀和。
  • 然后,我们可以用⼀个 for 循环枚举可能的中心下标,判断每⼀个位置的「前缀和」以及「后缀和」,如果二者相等,就返回当前下标。
class Solution {public int pivotIndex(int[] nums) {int n = nums.length;int[] f= new int[n];int[] g= new int[n];//1.预处理一下前缀和数组以及后缀和数组for(int i = 1; i < n; i++) {f[i] = f[i-1] + nums[i-1];}for(int i = n - 2; i >= 0; i--) {g[i] = g[i+1] + nums[i+1];}//2.使用for(int i = 0; i < n; i++) {if(f[i] == g[i]) {return i;}}return -1;}
}

还可以这样:

class Solution {public int pivotIndex(int[] nums) {int total = 0;for(int i = 0; i < nums.length; ++i) {total += nums[i];}int sum = 0;for (int i = 0; i < nums.length; ++i) {if (2 * sum + nums[i] == total) {return i;}sum += nums[i];}return -1;}
}

除自身以外数组的乘积

算法思路:
注意题目的要求,不能使用除法,并且要在 O(N) 的时间复杂度内完成该题。那么我们就不能使用暴力的解法,以及求出整个数组的乘积,然后除以单个元素的方法(元素如果有0的话还得特殊讨论)。

根据题意,对于每⼀个位置的最终结果 ret[i] ,它是由两部分组成的:

  1. nums[0] * nums[1] * nums[2] * … * nums[i - 1]
  2. nums[i + 1] * nums[i + 2] * … * nums[n - 1]

于是,我们可以利用前缀和的思想,使用两个数组 f 和 g,分别处理出来两个信息:

  • f 表示:i 位置之前的所有元素的前缀乘积,
  • g表示: i 位置之后的所有元素的后缀乘积

然后再处理最终结果。

class Solution {public int[] productExceptSelf(int[] nums) {int n = nums.length;int[] f = new int[n];int[] g = new int[n];//1.预处理前缀积数组以及后缀积数组f[0] = g[n-1] = 1;for(int i = 1; i < n; i++) {f[i] = f[i-1] * nums[i-1];}for(int i = n-2; i >= 0; i--) {g[i] = g[i+1] * nums[i+1];}//2.使用int[] ret = new int[n];for(int i = 0; i < n ; i++) {ret[i] = f[i] * g[i];}return ret;}
}

简化版本:

class Solution {public int[] productExceptSelf(int[] nums) {int n = nums.length;int[] ret = new int[n];ret[0] = 1;for (int i = 1; i < n; i++) {ret[i] = nums[i - 1] * ret[i - 1];}int R = 1;for (int i = n - 1; i >= 0; i--) {ret[i] = ret[i] * R;R *= nums[i];}return ret;}
}

和为k的子数组

因为元素中有0和负数,不能用滑动窗口解决

算法思路:

设 i 为数组中的任意位置,用 sum[i] 表示 [0, i] 区间内所有元素的和。
想知道有多少个「以 i 为结尾的和为 k 的子数组」,就要找到多少个起始位置为 x 使得 [x, i] 区间内所有元素的和为 k 。那么 [0, x - 1] 区间内的和就是sum[i] - k 了。于是问题就变成:找到 [0, i - 1] 区间内,有多少前缀和等于 sum[i] - k 即可。
我们不用真的初始化⼀个前缀和数组,因为我们只关心在 i 位置之前,有多少个前缀和等于sum[i] - k 。因此,我们仅需用⼀个哈希表,⼀边求当前位置的前缀和,⼀边存下之前的前缀和。

class Solution {public int subarraySum(int[] nums, int k) {Map<Integer, Integer> hash = new HashMap<>();hash.put(0,1);int sum = 0, ret = 0;for(int x: nums) {sum += x;ret += hash.getOrDefault(sum-k, 0);hash.put(sum, hash.getOrDefault(sum, 0) + 1);}return ret;}
}

和可被K整除的子数组

本题需要的前置知识:

  • 同余定理

如果 (a - b) % n == 0 ,那么我们可以得到⼀个结论: a % n == b % n 。
因为(a - b) % n == 0, 所以 (a -b) ÷ n = k , 也就是a = b+ n × k, 两边同时%n
得到a % n == b % n

  • Java 中负数取模的结果,以及如何修正「负数取模」的结果:

Java 中关于负数的取模运算,结果是「把负数当成正数,取模之后的结果加上⼀个负号」。

	public static void main(String[] args) {System.out.println(-7%3);}

在这里插入图片描述

为了防止「出现负数」,以 (a % n + n) % n 的形式保证输出为正。

	public static void main(String[] args) {System.out.println((-7%3+3)%3);}

在这里插入图片描述
算法思路:

思路与和为 K 的子数组相似。
设 i 为数组中的任意位置,用sum[i] 表示 [0, i] 区间内所有元素的和。

  • 想知道有多少个「以 i 为结尾的可被 k 整除的子数组」,就要找到多少个起始位置为 x使得 [x, i] 区间内所有元素的和可被 k 整除。
  • 设 [0, x - 1] 区间内所有元素之和等于 a , [0, i] 区间内所有元素的和等于 b ,可得
    (b - a) % k == 0 。
  • 由同余定理可得, [0, x - 1] 区间与 [0, i] 区间内的前缀和同余。于是问题就变成:找到[0, i - 1] 区间内,有多少前缀和k的余数等于 sum[i] % k 的即可。
  • 我们不用真的初始化⼀个前缀和数组,因为我们只关心在 i 位置之前,有多少个前缀和等于sum[i] % k 。因此,我们仅需用⼀个哈希表,⼀边求当前位置的前缀和,⼀边存下之前的前缀和。
class Solution {public int subarraysDivByK(int[] nums, int k) {Map<Integer, Integer> hash = new HashMap<>();hash.put(0 % k, 1);int sum = 0, ret = 0;for(int x: nums){sum += x;int r = (sum % k + k) % k;ret += hash.getOrDefault(r, 0);hash.put(r, hash.getOrDefault(r, 0) + 1);}return ret;}
}

连续数组

算法思路:

本题让我们找出⼀段连续的区间, 0 和 1 出现的次数相同。

  • 如果将 0 记为 -1 , 1 记为 1 ,问题就变成了找出⼀段区间,这段区间的和等于 0 。
  • 于是,就和 和为 K 的子数组 的思路⼀样

设 i 为数组中的任意位置,用 sum[i] 表示 [0, i] 区间内所有元素的和。

想知道最大的「以 i 为结尾的和为 0 的子数组」,就要找到 x 使得 [x, i]区间内的所有元素的和为 0 。那么 [0, x - 1] 区间内元素的和就是 sum[i] 了。于是问题就变成:找到在 [0, i - 1] 区间内,第⼀次出现 sum[i] 的位置即可。
我们不用真的初始化⼀个前缀和数组,我们只关心在 i 位置之前,第⼀个前缀和等于 sum[i]的位置。因此,我们仅需用⼀个哈希表,⼀边求当前位置的前缀和,⼀边记录第⼀次出现该前缀和的位置。

class Solution {public int findMaxLength(int[] nums) {Map<Integer, Integer> hash = new HashMap<>();hash.put(0, -1);//默认存在一个前缀和为0的情况int sum = 0, ret = 0;for(int i = 0; i < nums.length; i++) {sum += (nums[i] == 0 ? -1 : 1);//计算当前位置的前缀和if(hash.containsKey(sum)) ret = Math.max(ret, i - hash.get(sum));else hash.put(sum, i);}return ret;}
}

矩阵区域和

算法思路:

二维前缀和,关键是找到原矩阵对应区域的「左上角」以及「右下角」的坐标(推荐大家画图)
左上角坐标: x1 = i - k,y1 = j - k ,但是由于会「超过矩阵」的范围,因此需要和 0 比较取⼀个 max 。

右下角坐标: x2 = i + k,y2 = j + k ,但是由于会「超过矩阵」的范围,因此需要比较 m - 1 以及 n - 1 取⼀个 min 。因此修正后的坐标为: x2 = min(m - 1, i + k), y2 = min(n - 1, j + k) 。

然后将求出来的坐标代⼊到「二维前缀和矩阵」的计算公式上即可

class Solution {public int[][] matrixBlockSum(int[][] mat, int k) {int m = mat.length, n = mat[0].length;//1.预处理前缀和矩阵int[][] dp = new int[m+1][n+1];for(int i = 1; i <= m; i++) {for(int j = 1; j <= n; j++) {dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + mat[i-1][j-1];}}//2.使用int[][] ret = new int [m][n];for(int i = 0; i < m; i++) {for(int j = 0; j < n; j++) {int x1 = Math.max(0, i-k) + 1, y1 = Math.max(0, j-k) + 1;int x2 = Math.min(m-1, i + k) + 1, y2 = Math.min(n-1, j+k) + 1;ret[i][j] = dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1]; }}return ret;}
}

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

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

相关文章

打卡学习kubernetes——了解k8s基本概念

目录 1 Container 2 Pod 3 Node 4 Namespace 5 Service 6 Label 7 Annotations 8 Volume 1 Container Container(容器)是一种便携式、轻量级的操作系统级虚拟化技术。它使用namespace隔离不同的软件运行环境&#xff0c;并通过镜像自包含软件的运行环境&#xff0c;从而…

关于继承是怎么样的?那当然是很好理解之

本文描述了关于继承的大部分知识&#xff0c;但是并不全&#xff0c;每篇博客之间的知识都有互串&#xff0c;所以需要把几篇文章合起来看&#xff0c;学会融会贯通&#xff01; 温馨提示&#xff1a;使用PC端观看&#xff0c;效果更佳&#xff01; 目录 1.继承是什么 2.什…

机器学习是什么?

机器学习是一种人工智能&#xff08;AI&#xff09;的分支&#xff0c;其主要目标是使计算机系统能够通过数据和经验来改进和学习&#xff0c;而无需明确地编程。在机器学习中&#xff0c;计算机系统会通过对大量数据进行学习和分析&#xff0c;从中发现模式和规律&#xff0c;…

JavaScript进阶:js的一些学习笔记-4

文章目录 1. 拷贝1. 浅拷贝2. 深拷贝 2. 异常处理 1. 拷贝 这里指的拷贝是指拷贝引用类型的数据(对象) 1. 浅拷贝 拷贝对象&#xff1a;Object.assign() 或者 {…obj} 展开运算符 const obj {name:liuze,age:23 } const o {...obj}; o.age 22; console.log(o); console.…

Linux | Ubuntu安装pylsl

PYNQ开发中使用pylsl过程记录 操作系统为 Linux pynq 5.15.19-xilinx-v2022.1 #1 SMP PREEMPT Mon Apr 11 17:52:14 UTC 2022 armv7l armv7l armv7l GNU/Linux 使用 pip install pylsl 安装后在导入包的过程中会遇到如下错误&#xff1a; RuntimeError: LSL binary library f…

深入浅出前端本地储存(1)

引言 2021 年&#xff0c;如果你的前端应用&#xff0c;需要在浏览器上保存数据&#xff0c;有三个主流方案&#xff1a; CookieWeb Storage (LocalStorage)IndexedDB 这些方案就是如今应用最广、浏览器兼容性最高的三种前端储存方案 今天这篇文章就聊一聊这三种方案的历史…

前端基础篇-深入了解 Ajax 、Axios

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 Ajax 概述 2.0 Axios 概述 3.0 综合案例 1.0 Ajax 概述 通过 Ajax 可以给服务器发送请求&#xff0c;并获取服务器响应的数据。异步交互是指&#xff0c;可以在不…

【安全类书籍-2】Web渗透测试:使用Kali Linux

目录 内容简介 作用 下载地址 内容简介 书籍的主要内容是指导读者如何运用Kali Linux这一专业的渗透测试平台对Web应用程序进行全面的安全测试。作者们从攻击者的视角出发,详细阐述了渗透测试的基本概念和技术,以及如何配置Kali Linux以适应渗透测试需求。书中不仅教授读者…

[zdyz]FreeRTOS笔记

FreeRTOS基础知识 1&#xff0c;任务调度器简介 调度器就是使用相关的调度算法来决定当前需要执行的哪个任务 抢占式调度 时间片调度 协程式调度 略 2&#xff0c;任务状态 运行态 正在执行的任务&#xff0c;该任务就处于运行态&#xff0c;注意在STM32中&#xff0c;同…

【JAVA】Servlet开发

目录 HttpServlet HttpServletRequest HttpServletResponse 错误页面 设置网页自动刷新时间 构造重定向相应 js发起http请求 服务器端对js发起的http请求进行处理 前端获取后端数据&#xff0c;添加到当前页面的末尾&#xff0c;代码示例&#xff1a; 前后端交互&…

Linux环境(Ubuntu)上搭建MQTT服务器(EMQX )

目录 概述 1 认识EMQX 1.1 EMQX 简介 1.2 EMQX 版本类型 2 Ubuntu搭建EMQX 平台 2.1 下载和安装 2.1.1 下载 2.1.2 安装 2.2 查看运行端口 3 运行Dashboard 管理控制台 3.1 查看Ubuntu上的防火墙 3.2 运行Dashboard 管理控制台 概述 本文主要介绍EMQX 的一些内容&a…

深入解析C++树形关联式容器:map、set及其衍生容器的使用与原理

文章目录 一、引言二、关联式容器的中的 paira.pair 的创建及使用b.pair 间的比较 三、 map 与 set 详解1. map 的基本操作2. set 的基本操作3.关联式容器的迭代器 四、 multimap 与 multiset 的特性五、关联式容器的使用技巧与注意事项1. 键值类型的选择与设计2. 自定义比较函…

SVN修改已提交版本的注释

目录 一、需求分析 二、问题分析 三、解决办法 一、需求分析 ​开发过程中&#xff0c;在SVN提交文件后&#xff0c;发现注释写的不完整或不够明确&#xff0c;想再修改之前的注释文字​。 使用环境&#xff1a; SVN服务器操作系统&#xff1a;Ubuntu 20.04.6 LTS SVN版本&…

vr虚拟现实游戏世界介绍|数字文化展览|VR元宇宙文旅

虚拟现实&#xff08;VR&#xff09;游戏世界是一种通过虚拟现实技术创建的沉浸式游戏体验&#xff0c;玩家可以穿上VR头显&#xff0c;仿佛置身于游戏中的虚拟世界中。这种技术让玩家能够全方位、身临其境地体验游戏&#xff0c;与游戏中的环境、角色和物体互动。 在虚拟现实游…

Android14 - AMS之Activity启动过程(3)

Android14 - AMS之Activity启动过程&#xff08;1&#xff09;-CSDN博客 Android14 - AMS之Activity启动过程&#xff08;2&#xff09;-CSDN博客 上篇中我们梳理完ActivityStarter的startActivityInner&#xff0c;本篇从这里开始&#xff1a; platform/frameworks/base/servi…

VC6环境开发汇编程序和汇编语言调用C库

新建一个Win32控制台类型的空项目&#xff1b; 新建一个源文件&#xff0c;输入文件名时输入后缀.asm&#xff1b;.asm后缀的文件如果不会出现在Source Files文件夹下&#xff0c;可将其拖放到Source Files文件夹下&#xff1b; 输入如下代码&#xff1b;调用C的printf函数输出…

UE5.1 iClone8 正确导入角色骨骼与动作

使用iClone8插件Auto Setup 附录下载链接 里面有两个文件夹,使用Auto Setup C:\Program Files\Reallusion\Shared Plugins 在UE内新建Plugins,把插件复制进去 在工具栏出现这三个人物的图标就安装成功了 iClone选择角色,导入动作 选择导出FBX UE内直接导入 会出现是否启动插件…

Vue 计算属性和监视属性

Vue 计算属性和监视属性 computed computed 计算属性 规则&#xff1a; 用已有的属性计算不存在的属性默认调用一次get()只有值不发生改变的时候才可以使用简写&#xff08;函数&#xff09;&#xff1b;值发生改变 使用对象式写法&#xff0c;才可以配置set()方法底层原理使…

计算机视觉之三维重建(2)---摄像机标定

文章目录 一、回顾线代1.1 线性方程组的解1.2 齐次线性方程组的解 二、透镜摄像机的标定2.1 标定过程2.2 提取摄像机参数2.3 参数总结 三、径向畸变的摄像机标定3.1 建模3.2 求解 四、变换4.1 2D平面上的欧式变换4.2 2D平面上的相似变换和仿射变换4.3 2D平面上的透射变换4.4 3D…

Git Bash命令初始化本地仓库,提交到远程仓库

git init&#xff1a;初始化空仓库 // 初始化一个空仓库或者重新初始化一个存在的仓库 git init git remote // 为当前本地仓库添加一个远程仓库地址 git remote add origin https://gitee.com/xxx/demo.git git pull // 从设置好链接的远程仓库拉去已经存在的数据&#xff0c;…