算法【Java】 —— 前缀和
模板引入
一维前缀和
https://www.nowcoder.com/share/jump/9257752291725692504394

解法一:暴力枚举
在每次提供 l 与 r 的时候,都从 l 开始遍历数组,直到遇到 r 停止,这个方法的时间复杂度为 O(N * q)
解法二:前缀和
如果我们事先就知道所有从 0 开始的子数组的前缀和的话,那么要计算 从 l 到 r 之间的字数组的和的时候,我们就可以利用这个已知的前缀和数组进行计算,也就是 ans = dp[r] - dp[l] + nums[l]
时间复杂度为 O(N + q) , 空间复杂度为 O(N)
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[] arr = new long[n];for(int i = 0; i < n; i++) {arr[i] = in.nextInt();}//构建前缀和数组long[] dp = new long[n+1];for(int i = 1; i <= n; i++) {dp[i] = dp[i-1] + arr[i-1];}while(q > 0) {int l = in.nextInt();int r = in.nextInt();System.out.println(dp[r] - dp[l] + arr[l-1]);q--;}}
}
二维前缀和
https://www.nowcoder.com/share/jump/9257752291725693083065


解法一:暴力枚举
直接遍历矩阵,时间复杂度为 O(N² * q)
解法二:前缀和
和上面那一道题目一样,我们先预处理一个前缀和数组,只不过这次是一个二维数组,我们需要讨论一下:

由上图可知,要想求 dp[i][j],我们可以利用上面的关系来推导,也就是 dp[i][j] 等于三块颜色的区域 加上 原矩阵对应的 nums[i][j]
dp[i-1][j] 等于蓝色区域加上绿色区域,dp[i][j-1] 等于蓝色区域加上橙色区域,dp[i-1][j-1] 等于蓝色区域
dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + nums[i][j]
但是要注意可能会出现数组越界访问的情况,因为当 i = 0 或者 j = 0 的时候 ,dp[i][j-1] 是会发生越界访问的,所以要避免发生越界访问的话,我们可以加多一个外围区域,就是将 dp 数组 在创建的时候,可以将 dp[][] = new int[n+1][m+1],也就是比原先数组多一行和多一列
这样的话,我们在进行 dp 数组计算的时候,下标就应该从 1 开始,对应的nums 数组的下标则是减一即可。
dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + nums[i-1][j-1]
如何获取从 (x1,y1) 到 (x2,y2) 之间的区域的和

因为正好是x1 ,y1, x2, y2 可以对应我们的 dp 数组,所以直接使用
蓝色区域是我们要求的区域,蓝色区域 等于 = 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);int n = in.nextInt();int m = in.nextInt();int q = in.nextInt();int[][] arr = new int[n][m];for(int i = 0; i < n; i++) {for(int j = 0; j < m; j++) {arr[i][j] = in.nextInt();}}//构建前缀和数组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][j-1] + dp[i-1][j] - dp[i-1][j-1] + arr[i-1][j-1];}}while(q > 0) {int x1 = in.nextInt();int y1 = in.nextInt();int x2 = in.nextInt();int y2 = in.nextInt();System.out.println(dp[x2][y2] - (dp[x1-1][y2] + dp[x2][y1-1] - dp[x1-1][y1-1]));q--;}}
}
小结
前缀和主要用于处理数组区间的和的问题。
前缀和处理二维数组(矩阵)的时候,要注意 dp 数组要扩大。
实战演练
寻找数组的中心下标
https://leetcode.cn/problems/find-pivot-index/description/

解析:
中心下标表示除了这个下标对应的数字之外,其左边区域和等于右边区域和
区域和,我们可以联想到前缀和来解决,由于需要比较左右两边的区域和,我们可以设置前缀和数组与后缀和数组。
注意事项
前缀和应该从 下标 1 开始计算,f[i] = f[i-1] + nums[i-1]
后缀和应该从下标 n - 2 开始计算,g[i] = g[i+1] + nums[i+1]
因为上面两条公式需要利用到之前的前缀和或者后缀和,如果前缀和从0开始计算,则会数组越界访问异常,并且题目告诉我们如果中心下标是最左端,则左侧数组和为 0 ,右侧也一样,所以我们可以直接从 0 或者 n - 2 开始计算。
class Solution {public int pivotIndex(int[] nums) {int n = nums.length;int[] f = new int[n];int[] g = new int[n];//构建前缀和数组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];}//查找for(int i = 0; i < n; i++) {if(f[i] == g[i])return i;}return -1;}
}
除自身以外数组的乘积
https://leetcode.cn/problems/product-of-array-except-self/description/

解析:
和上面的题目类似,这次是乘法,我们可以使用前缀积和后缀积来解答
这里要注意 f[0] 和 g[n-1] 要先预设置为 0,避免后续计算的时候得到的全是 0
class Solution {public int[] productExceptSelf(int[] nums) {int n = nums.length;int[] f = new int[n];int[] g = new int[n];f[0] = 1;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];}int[] ans = new int[n];for(int i = 0; i < n; i++) {ans[i] = f[i] * g[i];}return ans;}
}
和为 K 的子数组
https://leetcode.cn/problems/subarray-sum-equals-k/description/

注意这里不能使用滑动窗口来解决!!!
滑动窗口有一个特点就是右指针不能进行回退,所以当你要使用滑动窗口来解决子数组的时候,要保证一个前提:数组具有单调性。
而这道题目由于数组可能会出现负数,也就是说明数组是不具有单调性的,举个例子:

两块蓝色区域相加正好等于零,而中间红色区域等于K 的时候,你使用滑动窗口,由于右指针不能回退,导致遗漏掉中间红色数组的情况,所以,我们不能使用滑动窗口。
解法一:暴力枚举
通过两层 循环,获取所有的子数组,时间复杂度为 O(N²)
解法二:前缀和加哈希表
计算 [0, i] 区间的前缀和 sum[i]
我们把要求的 K 子数组定义为以 i 结尾的子数组
如果存在 K 的子数组的话:

那么如果 i 数组前面出现过 子数组和为 sum[j] 的话,就说明存在 K 数组,sum[j] = sum[i] - K
我们在计算 前缀和 sum[i] 的时候,其实也就已经得知 i 数组前面所有的下标的前缀和,那么我们可以利用哈希表来保存这些数据,哈希表存储的应该是 <sum[i], 出现的次数>
我们其实不需要创建前缀和数组
因为我们只是要利用前一个前缀和结果来推导现在这一个前缀和,对于前几次的前缀和结果我们其实用不到,所以我们可以使用一个变量 sum,一直滚动累加下去即可。
要事先将 <0,1> 存进哈希表中
因为可能会出现一开始就得到 和为 K 的数组,此时 sum - k = 0,但是 count 却没有自增,所以我们事先设定存在一个前缀和 为 0 的子数组,并且出现次数为 1
class Solution {public int subarraySum(int[] nums, int k) {Map<Integer,Integer> map = new HashMap<>();map.put(0,1);int sum = 0;int count = 0;for(int x : nums) {sum += x;if(map.containsKey(sum - k)) {count += map.get(sum-k);}map.put(sum,map.getOrDefault(sum,0) + 1);}return count;}
}
和可被 K 整除的子数组
https://leetcode.cn/problems/subarray-sums-divisible-by-k/description/

解析:
这里依旧不能使用滑动窗口来做
因为存在负数,不具有单调性。
补充知识:同余定理,当 (a - b) % p = 0 可以推导出 a % p = b % p,有了这个公式,我们就可以得出当存在另外几个 b 满足 a % p = b % p 的时候,则说明存在 和可被 K 整除的子数组,在判断的时候,我们用到的是余数,所以在哈希表中,我们保存的应该为 <余数,余数出现的次数>,后面就是基本的前缀和操作了,和上面那一道题目类似。
Java 当中,负数 % 正数 的结果为 负数,我们需要将结果纠正为正数,公式为 ret = (sum % k + k) % k
解释一下,sum % k 可以得到余数,但是可能为负数,所以再加 一个 k,保证这是一个正数,但是可能会破坏余数这个特性,所以还要再次 模 k,最后的 模k 是不会影响的。
由于可能会出现一开始前缀和 正好为 K ,取模结果为0,此时应该预先将 <0,1> 存储进哈希表里。
class Solution {public int subarraysDivByK(int[] nums, int k) {Map<Integer,Integer> map = new HashMap<>();map.put(0,1);int sum = 0;int count = 0;for(int x : nums) {sum += x;int ret = (sum % k + k) % k;count += map.getOrDefault(ret, 0);map.put(ret,map.getOrDefault(ret,0) + 1);}return count;}
}
连续数组
https://leetcode.cn/problems/contiguous-array/description/

解析:
由于数组只会出现 0 或者 1,我们要寻找最大长度的数组(满足 0 的数量等于 1 的数量),如果要使用前缀和,我们可以将 0 设置为 -1,这样,我们要寻找的子数组(记为 K)的和就是为 0
前缀和数组sum[i] :

此时 K = 0,那么 就是要得到目前是否存在 sum[j] 数组是等于 sun[i],如果存在,则需要计算这个 K 数组的区间长度 = i - j
于是我们可以将前缀和 与 对应的下标索引 存到哈希表里,<sum,下标>
我们不用真的创建一个前缀和数组,因为我们用不到,我们可以使用一个变量 sum 来保存当前的前缀和结果
如果当整个数组的和为 K 的时候,那么最大的长度应该为 整个数组的长度,此时 i 最大到达 nums.length - 1 ,要想获得 数组长度,我们应该预先将 <0, -1> 存储进哈希表中
哈希表的 put :
我们保存数据的时候,要保存的是 sum 对应的最小的索引,因为我们要求的是最大的数组长度,所以当哈希表存在 sum[i] 的时候,我们应该更新最新长度,这个时候,就不用保存进哈希表里了,因为我们不需要跟新下标索引值,如果没有出现的话,那么此时 sum[i] 所对应的下标要一起保存进哈希表中。
class Solution {public int findMaxLength(int[] nums) {Map<Integer,Integer> map = new HashMap<>();map.put(0,-1);int n = nums.length;int sum = 0;int maxLength = 0;for(int i = 0; i < n; i++) {sum += (nums[i] == 0 ? -1 : 1);if(map.containsKey(sum)) {int j = map.get(sum);maxLength = Math.max(maxLength,i - j);} else {map.put(sum,i);}}return maxLength;}
}
矩阵区域和
https://leetcode.cn/problems/matrix-block-sum/description/

解析:
这道题目就是二维数组前缀和的使用
我们先创建好前缀和数组,然后锁定求解的区域,最后计算即可
这里要注意的是前缀和数组要和我们的最终数组要一一对应
class Solution {public int[][] matrixBlockSum(int[][] mat, int k) {int n = mat.length;int m = mat[0].length;int[][] dp = new int[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] - dp[i-1][j-1] + mat[i-1][j-1];}}int[][] ans = new int[n][m];for(int i = 0; i < n; i++) {for(int j = 0; j < m; j++) {int r1 = (i - k <= 0 ? 1 : i - k + 1);int c1 = (j - k <= 0 ? 1 : j - k + 1);int r2 = (i + k >= n ? n : i + k + 1);int c2 = (j + k >= m ? m : j + k + 1);ans[i][j] = dp[r2][c2] - (dp[r1-1][c2] + dp[r2][c1-1] - dp[r1-1][c1-1]);}}return ans;}
}
相关文章:
算法【Java】 —— 前缀和
模板引入 一维前缀和 https://www.nowcoder.com/share/jump/9257752291725692504394 解法一:暴力枚举 在每次提供 l 与 r 的时候,都从 l 开始遍历数组,直到遇到 r 停止,这个方法的时间复杂度为 O(N * q) 解法二:前…...
python网络爬虫(四)——实战练习
0.为什么要学习网络爬虫 深度学习一般过程: 收集数据,尤其是有标签、高质量的数据是一件昂贵的工作。 爬虫的过程,就是模仿浏览器的行为,往目标站点发送请求,接收服务器的响应数据,提取需要的信息,…...
tio websocket 客户端 java 代码 工具类
为了更好地组织代码并提高可复用性,我们可以将WebSocket客户端封装成一个工具类。这样可以在多个地方方便地使用WebSocket客户端功能。以下是使用tio库实现的一个WebSocket客户端工具类。 1. 添加依赖 确保项目中添加了tio的依赖。如果使用的是Maven,可以…...
通过卷积神经网络(CNN)识别和预测手写数字
一:卷积神经网络(CNN)和手写数字识别MNIST数据集的介绍 卷积神经网络(Convolutional Neural Networks,简称CNN)是一种深度学习模型,它在图像和视频识别、分类和分割任务中表现出色。CNN通过模仿…...
【A题第二套完整论文已出】2024数模国赛A题第二套完整论文+可运行代码参考(无偿分享)
“板凳龙” 闹元宵路径速度问题 摘要 本文针对传统舞龙进行了轨迹分析,并针对一系列问题提出了解决方案,将这一运动进行了模型可视化。 针对问题一,我们首先对舞龙的螺线轨迹进行了建模,将直角坐标系转换为极坐标系࿰…...
一份热乎的数据分析(数仓)面试题 | 每天一点点,收获不止一点
目录 1. 已有ods层⽤⼾表为ods_online.user_info,有两个字段userid和age,现设计数仓⽤⼾表结构如 下: 2. 设计数据仓库的保单表(⾃⾏命名) 3. 根据上述两表,查询2024年8⽉份,每⽇,…...
3 html5之css新选择器和属性
要说css的变化那是发展比较快的,新增的选择器也很多,而且还有很多都是比较实用的。这里举出一些案例,看看你平时都是否用过。 1 新增的一些写法: 1.1 导入css 这个是非常好的一个变化。这样可以让我们将css拆分成公共部分或者多…...
【Kubernetes】K8s 的鉴权管理(一):基于角色的访问控制(RBAC 鉴权)
K8s 的鉴权管理(一):基于角色的访问控制(RBAC 鉴权) 1.Kubernetes 的鉴权管理1.1 审查客户端请求的属性1.2 确定请求的操作 2.基于角色的访问控制(RBAC 鉴权)2.1 基于角色的访问控制中的概念2.1…...
保研 比赛 利器: 用AI比赛助手降维打击数学建模
数学建模作为一个热门但又具有挑战性的赛道,在保研、学分加分、简历增色等方面具有独特优势。近年来,随着AI技术的发展,特别是像GPT-4模型的应用,数学建模的比赛变得不再那么“艰深”。通过利用AI比赛助手,不仅可以大大…...
秋招校招,在线性格测评应该如何应对
秋招校招,如果遇到在线测评,如何应对? 这里写个总结稿,希望对大家有些帮助。在线测评是企业深入了解求职人的渠道,如果是性格测试,会要求测试者能够快速答出,以便于反应实际情况(时间…...
chrome 插件开发入门
1. 介绍 Chrome 插件可用于在谷歌浏览器上控制当前页面的一些操作,可自主控制网页,提升效率。 平常我们可在谷歌应用商店中下载谷歌插件来增强浏览器功能,作为开发者,我们也可以自己开发一个浏览器插件来配合我们的日常学习工作…...
揭开面纱--机器学习
一、人工智能三大概念 1.1 AI、ML、DL 1.1.1 什么是人工智能? AI:Artificial Intelligence 人工智能 AI is the field that studies the synthesis and analysis of computational agents that act intelligently AI is to use computers to analog and instead…...
Python中的私有属性与方法:解锁面向对象编程的秘密
在Python的广阔世界里,面向对象编程(OOP)是一种强大而灵活的方法论,它帮助我们更好地组织代码、管理状态,并构建可复用的软件组件。而在这个框架内,私有属性与方法则是实现封装的关键机制之一。它们不仅有助…...
开篇_____何谓安卓机型“工程固件” 与其他固件的区别 作用
此系列博文将分析安卓系列机型与一些车机 wifi板子等工程固件的一些常识。从早期安卓1.0起始到目前的安卓15,一些厂家发布新机型的常规流程都是从工程机到量产的过程。在其中就需要调试各种参数以便后续的量产参数可以固定到最佳,工程固件由此诞生。 后…...
DBeaver 连接 MySQL 报错 Public Key Retrieval is not allowed
DBeaver 连接 MySQL 报错 Public Key Retrieval is not allowed 文章目录 DBeaver 连接 MySQL 报错 Public Key Retrieval is not allowed问题解决办法 问题 使用 DBeaver 连接 MySQL 数据库的时候, 一直报错下面的错误 Public Key Retrieval is not allowed详细…...
三个月涨粉两万,只因为知道了这个AI神器
大家好,我是凡人,最近midjourney的账号到期了,正准备充值时,被一个国内AI图片的生成神器给震惊了,不说废话,先上图看看生成效果。 怎么样还不错吧,是我非常喜欢的国风画,哈哈&#x…...
vulhub GhostScript 沙箱绕过(CVE-2018-16509)
1.搭建环境 2.进入网站 3.下载包含payload的png文件 vulhub/ghostscript/CVE-2018-16509/poc.png at master vulhub/vulhub GitHub 4.上传poc.png图片 5.查看创建的文件...
李宏毅机器学习笔记——反向传播算法
反向传播算法 反向传播(Backpropagation)是一种用于训练人工神经网络的算法,它通过计算损失函数相对于网络中每个参数的梯度来更新这些参数,从而最小化损失函数。反向传播是深度学习中最重要的算法之一,通常与梯度下降…...
内推|京东|后端开发|运维|算法...|北京 更多岗位扫内推码了解,直接投递,跟踪进度
热招岗位 更多岗位欢迎扫描末尾二维码,小程序直接提交简历等面试。实时帮你查询面试进程。 安全运营中心研发工程师 岗位要求 1、本科及以上学历,3年以上的安全相关工作经验; 2、熟悉c/c、go编程语言之一、熟悉linux网络编程和系统编程 3、…...
编写Dockerfile第二版
目标 更快的构建速度 更小的Docker镜像大小 更少的Docker镜像层 充分利用镜像缓存 增加Dockerfile可读性 让Docker容器使用起来更简单 总结 编写.dockerignore文件 容器只运行单个应用 将多个RUN指令合并为一个 基础镜像的标签不要用latest 每个RUN指令后删除多余文…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
Linux安全加固:从攻防视角构建系统免疫
Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...
【大模型】RankRAG:基于大模型的上下文排序与检索增强生成的统一框架
文章目录 A 论文出处B 背景B.1 背景介绍B.2 问题提出B.3 创新点 C 模型结构C.1 指令微调阶段C.2 排名与生成的总和指令微调阶段C.3 RankRAG推理:检索-重排-生成 D 实验设计E 个人总结 A 论文出处 论文题目:RankRAG:Unifying Context Ranking…...
精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑
精益数据分析(98/126):电商转化率优化与网站性能的底层逻辑 在电子商务领域,转化率与网站性能是决定商业成败的核心指标。今天,我们将深入解析不同类型电商平台的转化率基准,探讨页面加载速度对用户行为的…...
英国云服务器上安装宝塔面板(BT Panel)
在英国云服务器上安装宝塔面板(BT Panel) 是完全可行的,尤其适合需要远程管理Linux服务器、快速部署网站、数据库、FTP、SSL证书等服务的用户。宝塔面板以其可视化操作界面和强大的功能广受国内用户欢迎,虽然官方主要面向中国大陆…...
