前缀和算法

【模板】前缀和

image.png

题目链接:前缀和

算法思路

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

  • ⽤ dp[i] 表⽰: [1, i] 区间内所有元素的和,那么 dp[i - 1] ⾥⾯存的就是 [1, i - 1] 区间内所有元素的和,那么:可得递推公式: dp[i] = dp[i - 1] + arr[i] ;
  • 使⽤前缀和数组,「快速」求出「某⼀个区间内」所有元素的和: 当询问的区间是 [l, r] 时:区间内所有元素的和为: dp[r] - dp[l - 1] 。
代码
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scan = new Scanner(System.in);while (scan.hasNextInt()) { int n = scan.nextInt();int q = scan.nextInt();int[] array = new int[n + 1];for(int i = 1; i <= n; i++) {array[i] = scan.nextInt();}// 使用long防止溢出long dp[] = new long[n + 1];dp[0] = 0; // 初始化for(int i = 1; i <= n; i++) {// 前缀和dp[i] = dp[i - 1] + array[i];}for(int i = 0; i < q; i++) {int l = scan.nextInt();int r = scan.nextInt();// 使用前缀和数组System.out.println(dp[r] - dp[l - 1]);}}}
}

【模板】二维前缀和

image.png

题目链接:【模板】二维前缀和

算法思路:

image.png

代码:
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scan = new Scanner(System.in);int n = scan.nextInt();int m = scan.nextInt();int q = scan.nextInt();int[][] array = new int[n+1][m+1];for(int i = 1; i <= n; i++) {for(int j = 1; j <= m; j++) {array[i][j] = scan.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] + array[i][j];}}while(q > 0) {int x1 = scan.nextInt();int y1 = scan.nextInt();int x2 = scan.nextInt();int y2 = scan.nextInt();// 使用前缀和System.out.println(dp[x2][y2] - dp[x2][y1 - 1] - dp[x1 - 1][y2] + dp[x1-1][y1-1]);q--;}}
}

寻找数组的中心下标

image.png

题目链接:寻找数组的中心下标

算法思路:

image.png

代码:
class Solution {public int pivotIndex(int[] nums) {int n = nums.length;int[] ldp = new int[n+1];int[] rdp = new int[n+1];// 计算左前缀和for(int i = 1; i < n; i++) {// ldp[i]计算的是[0,i-1]之和ldp[i] = ldp[i - 1] + nums[i - 1];}   // 计算右前缀和    for(int i = n - 2; i >= 0; i--) {// rdp[i]计算的是[i+1, n-1]之和rdp[i] = rdp[i+1] + nums[i+1];}for(int i = 0; i < n; i++) {if(ldp[i] == rdp[i]) {return i;}}return -1;}
}

除自身以外数组的乘积

image.png

题目链接:除自身以外数组的乘积

算法思路:

image.png

代码:
class Solution {public int[] productExceptSelf(int[] nums) {// 题目要求不能使用除法// 可以前缀积*后缀积int n = nums.length;int[] ldp = new int[n+1];int[] rdp = new int[n+1];// 乘数不能为0ldp[0] = 1;rdp[n-1] = 1;// 求前缀积for(int i = 1; i < n; i++) {// ldp[i]等于[0, i-1]之间的乘积ldp[i] = ldp[i-1] * nums[i-1];}// 求后缀积for(int i = n - 2; i >= 0; i--) {// rdp[i]等于[i+1, n-1]之间的乘积rdp[i] = rdp[i+1] * nums[i+1];}int[] answer = new int[n];for(int i = 0; i < n; i++) {answer[i] = ldp[i] * rdp[i];}return answer;}
}

和为K的子数组

image.png

题目链接:和为k的子数组

算法思路:

image.png

代码:
class Solution {public int subarraySum(int[] nums, int k) {// 我的第一个想法是滑动窗口,但是不行,因为数组中有0和负数,不具有单调性。// 我们可以考虑前缀和// 求该数组中和为 k 的子数组的个数,即求sum - k有几个// 所以可以引入哈希表,统计前缀和的个数Map<Integer, Integer> hashMap = new HashMap<>();// 如果整个前缀和为k时hashMap.put(0, 1);int sum = 0;// 用来统计当前位置的前缀和int result = 0; // 用来统计个数for(int x : nums) {sum += x;// 更新结果result += hashMap.getOrDefault(sum - k, 0);// 把当前前缀和加入哈希表hashMap.put(sum, hashMap.getOrDefault(sum, 0) + 1);}return result;}
}

和可被K整除的子数组

image.png

题目链接:和可被K整除的子数组

算法思路:

image.png
设 i 为数组中的任意位置,⽤ sum[i] 表⽰ [0, i] 区间内所有元素的和。

  • 想知道有多少个「以 i 为结尾的可被 k 整除的⼦数组」,就要找到有多少个起始位置为 x1, x2, x3… 使得 [x, i] 区间内的所有元素的和可被 k 整除。
  • 设 [0, x - 1] 区间内所有元素之和等于 a , [0, i] 区间内所有元素的和等于 b ,可得 (b - a) % k == 0 。
  • 由同余定理可得, [0, x - 1] 区间与 [0, i] 区间内的前缀和同余。于是问题就变成: 找到在 [0, i - 1] 区间内,有多少前缀和的余数等于 sum[i] % k 的即可。

我们不⽤真的初始化⼀个前缀和数组,因为我们只关⼼在 i 位置之前,有多少个前缀和等于 sum[i] - k 。因此,我们仅需⽤⼀个哈希表,⼀边求当前位置的前缀和,⼀边存下之前每⼀种前缀和出现的次数。
image.png

代码:
class Solution {public int subarraysDivByK(int[] nums, int k) {Map<Integer,Integer> hashMap = new HashMap<>();// 整个前缀和为0hashMap.put(0%k,1);int sum = 0; // 求前缀和int result = 0; for(int x : nums) {sum += x; // 求当前位置的前缀和int r = (sum % k + k) % k; // 求余数// 更新结果result += hashMap.getOrDefault(r, 0);hashMap.put(r, hashMap.getOrDefault(r, 0) + 1);}return result;}
}

连续数组

image.png

题目链接:连续数组

算法思想:

image.png
设 i 为数组中的任意位置,⽤ sum[i] 表⽰ [0, i] 区间内所有元素的和。
想知道最⼤的「以 i 为结尾的和为 0 的⼦数组」,就要找到从左往右第⼀个 x1 使得 [x1,i] 区间内的所有元素的和为 0 。那么 [0, x1 - 1] 区间内的和是不是就是 sum[i] 了。于是问题就变成:

  • 找到在 [0, i - 1] 区间内,第⼀次出现 sum[i] 的位置即可。 我们不⽤真的初始化⼀个前缀和数组,因为我们只关⼼在 i 位置之前,第⼀个前缀和等于 sum[i] 的位置。因此,我们仅需⽤⼀个哈希表,⼀边求当前位置的前缀和,⼀边记录第⼀次出现该前缀和的位置。

image.png

代码:
class Solution {public int findMaxLength(int[] nums) {// 将0换成-1,即求和为0的最长连续子数组int n = nums.length;for(int i = 0; i < n; i++) {if(nums[i] == 0) {nums[i] = -1;}}Map<Integer, Integer> hash = new HashMap<>();hash.put(0, -1); // 前缀和为0int sum = 0;int result = 0;for(int i = 0; i < n; i++) {sum += nums[i];if(hash.containsKey(sum)) {result = Math.max(result, i - hash.get(sum));}else {// 第一次出现hash.put(sum, i);}}return result;}
}

矩阵区域和

image.png

题目链接:https://leetcode.cn/problems/matrix-block-sum/

算法思路:

image.png⼆维前缀和的简单应⽤题,关键就是我们在填写结果矩阵的时候,要找到原矩阵对应区域的「左上⻆」以及「右下⻆」的坐标(推荐⼤家画图)左上⻆坐标: x1 = i - k,y1 = j - k ,但是由于会「超过矩阵」的范围,因此需要对 0取⼀个 max 。因此修正后的坐标为: x1 = max(0, i - k), y1 = max(0, j - k) ;右下⻆坐标: x1 = i + k,y1 = 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;// 求二维前缀和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];}}// 使用二维前缀和int[][] answer = 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;answer[i][j] = dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1];}}return answer;}
}

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

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

相关文章

Leetcode刷题笔记题解(C++):257. 二叉树的所有路径

思路&#xff1a;深度优先搜索 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right…

Go语言每日一练——链表篇(五)

传送门 牛客面试笔试必刷101题 ----------------合并k个已排序的链表 题目以及解析 题目 解题代码及解析 解析 这一道题与昨天的合并链表题目类似&#xff0c;但是由于有K个且时间复杂度要求控制在O(nlogn)&#xff0c;这里主要有两种解法&#xff1a;一种是依旧使用归并来…

配置git环境与项目创建

项目设计 名称&#xff1a;KOB 项目包含的模块 PK模块&#xff1a;匹配界面&#xff08;微服务&#xff09;、实况直播界面&#xff08;WebSocket协议&#xff09; 对局列表模块&#xff1a;对局列表界面、对局录像界面 排行榜模块&#xff1a;Bot排行榜界面 用户中心模块&…

数据分析基础之《pandas(5)—文件读取与存储》

一、概述 1、我们的数据大部分存在于文件当中&#xff0c;所以pandas会支持复杂的IO操作&#xff0c;pandas的API支持众多文件格式&#xff0c;如CSV、SQL、XLS、JSON、HDF5 二、CSV 1、读取csv文件 read_csv(filepath_or_buffer, sep,, delimiterNone) 说明&#xff1a; fi…

Google Chrome Close AutoUpdate

DOMException: play() failed because the user didn‘t interact with the document first.-CSDN博客 html5 audio video-CSDN博客 Google Chrome Close AutoUpdate 关闭google浏览器自动更新 1&#xff1a;检查是否已安装google浏览器&#xff0c;并卸载&#xff1a; 2&…

黑马头条 Kafka

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 知…

【DC渗透系列】DC-2靶场

arp先扫 ┌──(root㉿kali)-[~] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:6b:ed:27, IPv4: 192.168.100.251 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.100.1 00:50:56:c0:00:08 VMware, In…

ElasticSearch查询语句用法

查询用法包括&#xff1a;match、match_phrase、multi_match、query_string、term 1.match 1.1 不同字段权重 如果需要为不同字段设置不同权重&#xff0c;可以考虑使用bool查询的should子句来组合多个match查询&#xff0c;并为每个match查询设置不同的权重 {"query&…

【征稿已开启】第五大数据、人工智能与软件工程国际研讨会(ICBASE 2024)

第五大数据、人工智能与软件工程国际研讨会&#xff08;ICBASE 2024&#xff09; 2024 5th International Conference on Big Data & Artificial Intelligence & Software Engineering 2024年09月20-22日 | 中国温州 第五届大数据、人工智能与软件工程国际研讨会&…

深信服技术认证“SCSA-S”划重点:安全事件管理处置

为帮助大家更加系统化地学习网络安全知识&#xff0c;以及更高效地通过深信服安全服务认证工程师考核&#xff0c;深信服特别推出“SCSA-S认证备考秘笈”共十期内容&#xff0c;“考试重点”内容框架&#xff0c;帮助大家快速get重点知识~ 划重点来啦 *点击图片放大展示 深信…

Unity笔记:相机移动

基础知识 鼠标输入 在Unity中&#xff0c;开发者在“Edit” > “Project Settings” > “Input Manager”中设置输入&#xff0c;如下图所示&#xff1a; 在设置了Mouse X后&#xff0c;Input.GetAxis("Mouse X")返回的是鼠标在X轴上的增量值。这意味着它会…

YOLOv5改进 | 细节涨点篇 | DySample一种超级轻量的动态上采样算子(效果完爆CARAFE)

一、 本文介绍 本文给大家带来的改进机制是一种号称超轻量级且有效的动态上采样器——DySample。与传统的基于内核的动态上采样器相比,DySample采用了一种基于点采样的方法,相比于以前的基于内核的动态上采样器,DySample具有更少的参数、浮点运算次数、GPU内存和延迟。此外…