1.【模板】前缀和
【模板】前缀和_牛客题霸_牛客网 (nowcoder.com)
1)题目描述
2)算法原理
解法二:前缀和->快速求出数组中某一个连续区间的和
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+1;i++){
arr[i]=in.nextInt();
}
//2.预处理一个前缀和数组
long[] dp=new long[n+1];
for(int i=1;i<n+1;i++){
dp[i]=dp[i-1]+arr[i];
}
//3.使用前缀和数组
while(q>0){
int left=in.nextInt(),right=in.nextInt();
System.out.println(dp[right]-dp[left-1]);
q--;
}
}
}
2.【模板】二维前缀和
【模板】二维前缀和_牛客题霸_牛客网 (nowcoder.com)
1)题目描述
2)算法原理
时间复杂度:O(mn)+O(q)
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();
//2.创建二维数组
int[][] arr=new int[n+1][m+1];
for(int i=1;i<n+1;i++){
for(int j=1;j<m+1;j++){
arr[i][j]=in.nextInt();
}
}
//3.创建全新的dp数组
long[][] dp=new long[n+1][m+1];
for(int i=1;i<n+1;i++){
for(int j=1;j<m+1;j++){
dp[i][j]=dp[i-1][j]+dp[i][j-1]+arr[i][j]-dp[i-1][j-1];
}
}
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--;
}
}
}
3.寻找数组的中心下标
1)题目描述
给你一个整数数组
nums
,请计算数组的 中心下标 。数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。
如果中心下标位于数组最左端,那么左侧数之和视为
0
,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回
-1
。示例 1:
输入:nums = [1, 7, 3, 6, 5, 6] 输出:3 解释: 中心下标是 3 。 左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 , 右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。示例 2:
输入:nums = [1, 2, 3] 输出:-1 解释: 数组中不存在满足此条件的中心下标。示例 3:
输入:nums = [2, 1, -1] 输出:0 解释: 中心下标是 0 。 左侧数之和 sum = 0 ,(下标 0 左侧不存在元素), 右侧数之和 sum = nums[1] + nums[2] = 1 + -1 = 0 。
2)算法原理
class Solution {
public int pivotIndex(int[] nums) {
int n=nums.length;
int[] dpf=new int[n];
int[] dpg=new int[n];
for(int i=1;i<n;i++){
dpf[i]=dpf[i-1]+nums[i-1];
}
for(int i=n-2;i>=0;i--){
dpg[i]=dpg[i+1]+nums[i+1];
}
for(int i=0;i<n;i++){
if(dpf[i]==dpg[i]){
return i;
}
}
return -1;
}
}
4.除自身以外数组的乘积
1)题目描述
238. 除自身以外数组的乘积 - 力扣(LeetCode)
给你一个整数数组
nums
,返回 数组answer
,其中answer[i]
等于nums
中除nums[i]
之外其余各元素的乘积 。题目数据 保证 数组
nums
之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。请 不要使用除法,且在
O(n)
时间复杂度内完成此题。示例 1:
输入: nums =[1,2,3,4]
输出:[24,12,8,6]
示例 2:
输入: nums = [-1,1,0,-3,3] 输出: [0,0,9,0,0]
2)算法原理
class Solution {
public int[] productExceptSelf(int[] nums) {
int n=nums.length;
int[] dpf=new int[n];
int[] dpg=new int[n];
int[] arr=new int[n];
dpf[0]=dpg[n-1]=1;
for(int i=1;i<n;i++){
dpf[i]=dpf[i-1]*nums[i-1];
}
for(int j=n-2;j>=0;j--){
dpg[j]=dpg[j+1]*nums[j+1];
}
for(int i=0;i<n;i++){
arr[i]=dpf[i]*dpg[i];
}
return arr;
}
}
5.和为k的子数组
1)题目描述
给你一个整数数组
nums
和一个整数k
,请你统计并返回 该数组中和为k
的子数组的个数 。子数组是数组中元素的连续非空序列。
示例 1:
输入:nums = [1,1,1], k = 2 输出:2示例 2:
输入:nums = [1,2,3], k = 3 输出:2
2)算法原理
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=sum+x;//计算当前位置的前缀和
ret=ret+hash.getOrDefault(sum-k,0);
hash.put(sum,hash.getOrDefault(sum,0)+1);
}
return ret;
}
}
6.和可被k整除的子数组
1)题目描述
974. 和可被 K 整除的子数组 - 力扣(LeetCode)
给定一个整数数组
nums
和一个整数k
,返回其中元素之和可被k
整除的非空 子数组 的数目。子数组 是数组中 连续 的部分。
示例 1:
输入:nums = [4,5,0,-2,-3,1], k = 5 输出:7 解释: 有 7 个子数组满足其元素之和可被 k = 5 整除: [4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]示例 2:
输入: nums = [5], k = 9 输出: 0
2)算法原理
1.同余定理
class Solution {
public int subarraysDivByK(int[] nums, int k) {
Map<Integer,Integer> hashMap=new HashMap<>();
hashMap.put(0%k,1);
int sum=0,ret=0;
for(int x:nums){
sum=sum+x;//计算当前位置的前缀和
int r=(sum%k+k)%k;
ret=ret+hashMap.getOrDefault(r,0);//统计结果
hashMap.put(r,hashMap.getOrDefault(r,0)+1);
}
return ret;
}
}
7.连续数组
1)题目描述
给定一个二进制数组
nums
, 找到含有相同数量的0
和1
的最长连续子数组,并返回该子数组的长度。示例 1:
输入: nums = [0,1] 输出: 2 说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。示例 2:
输入: nums = [0,1,0] 输出: 2 说明: [0, 1] (或 [1, 0]) 是具有相同数量0和1的最长连续子数组。
2)算法原理
class Solution {
public int findMaxLength(int[] nums) {
//1.重新处理数组
int n=nums.length;
int[] arr=new int[n];
for(int i=0;i<nums.length;i++){
if(nums[i]==0){
arr[i]=-1;
}else{
arr[i]=1;
}
}
//2.转换为处理前缀和
Map<Integer,Integer> hashMap=new HashMap<>();
hashMap.put(0,-1);//默认存在一个前缀和为0的情况
int sum=0,ret=0;
for(int i=0;i<arr.length;i++){
sum=sum+arr[i];//target=0,找到前缀和为sum的情况
if(hashMap.containsKey(sum)){
ret=Math.max(ret,i-hashMap.get(sum));
}else{
hashMap.put(sum,i);
}
// if(!hashMap.containsKey(sum)){
// hashMap.put(sum,i);
// }
}
return ret;
}
}
8.矩阵区域和
1)题目描述
给你一个
m x n
的矩阵mat
和一个整数k
,请你返回一个矩阵answer
,其中每个answer[i][j]
是所有满足下述条件的元素mat[r][c]
的和:
i - k <= r <= i + k,
j - k <= c <= j + k
且(r, c)
在矩阵内。示例 1:
输入:mat = [[1,2,3],[4,5,6],[7,8,9]], k = 1 输出:[[12,21,16],[27,45,33],[24,39,28]]示例 2:
输入:mat = [[1,2,3],[4,5,6],[7,8,9]], k = 2 输出:[[45,45,45],[45,45,45],[45,45,45]]
2)算法原理
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+1;i++){
for(int j=1;j<n+1;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;
}
}