4.优化算法之前缀和1

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)题目描述

【模板】二维前缀和_牛客题霸_牛客网 (nowcoder.com)

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)题目描述

560. 和为 K 的子数组 - 力扣(LeetCode)

给你一个整数数组 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)题目描述

525. 连续数组 - 力扣(LeetCode)

给定一个二进制数组 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)题目描述

1314. 矩阵区域和 - 力扣(LeetCode)

给你一个 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;
    }
}

  • 26
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值