【算法通关】前缀和:和为 K、和被 K整除、连续数组、矩阵区域和全解

文章目录

    • [1. 和为K的子数组](#1. 和为K的子数组)
    • [2. 和可被k整除的子数组](#2. 和可被k整除的子数组)
    • [3. 连续数组](#3. 连续数组)
    • [4. 矩阵区域和](#4. 矩阵区域和)

1. 和为K的子数组

题目链接:560. 和为K的子数组

题目描述:

算法思路:前缀和 + 哈希表

sumi表示(0,1)之间的前缀和,当x为起始位置,i元素结尾的数组和为k时,也就是相当于在(0,x)区间前缀和为sum - k,因此也就是求出在(0,i-1)内有多少个前缀和为sum - k 的数组即可。

但是也不需要真的定义一个前缀和,使用 sum 加等计算即可。前缀和处理的时机应该是遍历到哪个元素,就计算哪个元素之前的前缀和,因为当整个前缀和都预处理出来时,在哈希表中寻找可能会重复还未遍历到的元素的前缀和。

算法代码:

java 复制代码
	public int subarraySum(int[] nums, int k) {
        Map<Integer,Integer> hash = new HashMap<Integer,Integer>();
        hash.put(0,1);

        int sum = 0,ret = 0;
        for(int i = 0;i < nums.length;i++){
            sum += nums[i];
            ret += hash.getOrDefault(sum-k,0);
            hash.put(sum,hash.getOrDefault(sum,0)+1);
        }
        return ret;
    }

2. 和可被k整除的子数组

题目链接:974. 和可被k整除的子数组

题目描述:

算法思路:前缀和 + 哈希表

该题和上道题基本类似,只不过用到了同余定理:(a - b) % k == 0 得到 a % k == b % k,该题也就是找在(0,i-1)区间内有多少个前缀和的余数为sumi % k。

细节:前缀和中存的应该是前缀和的余数。

算法代码:

java 复制代码
	public int subarraysDivByK(int[] nums, int k) {
        Map<Integer,Integer> hash = new HashMap<Integer,Integer>();
        hash.put(0,1);

        int sum = 0, ret = 0;
        for(int i = 0; i < nums.length; i++){
            sum += nums[i];
            int r = (sum % k + k) % k;
            ret += hash.getOrDefault(r,0);
            hash.put(r,hash.getOrDefault(r,0)+1);
        }
        return ret;
    }

3. 连续数组

题目链接:525. 连续数组

题目描述:

算法思路:前缀和 + 哈希表

该题可进行一个转换处理,那就是将元素0转换为-1处理,那么0和1数量相同的子数组就是sum和为0的数组,此时就和上面的查找和为k的数组基本一样了,就是找和为0的数组。

细节问题:

此时哈希表中存的应该是sum和元素下标,因为该题返回的是数组长度。

当遇到重复的sum时,不需要重复的将下标存入哈希表,因为返回最长的数组长度,当前面有下标存入时,当遇到重复的sum,之前的下标一定小于现在的,因此可以跳过。

计算长度,当计算长度时需要判断当前的结果是否大于之前的结果。

还需要提前存入哈希表0的值的下标为-1,因为当整个数组为sum时,会去-1的位置找sum = 0,因此做提前处理。

算法代码:

java 复制代码
	public int findMaxLength(int[] nums) {
        Map<Integer,Integer> hash = new HashMap<Integer,Integer>();
        hash.put(0,-1);
        int ret = 0,sum = 0;
        for(int i = 0; i< nums.length; i++){
            if(nums[i] == 0) nums[i] = -1;
            sum += nums[i];
            if(hash.containsKey(sum)) ret = Math.max(ret,i - hash.get(sum));
            else hash.put(sum,i);
        }
        return ret;
    }

4. 矩阵区域和

题目链接:1314. 矩阵区域和

题目描述:

题意,如图:

answerij的值就是当前元素上下左右扩展k格的元素和。

算法思路:使用二维前缀和

首先预处理一个二维前缀和,和之前的二维前缀和定义相同。然后遍历元素计算该元素的answer,但是注意细节,当i-k,j-k时可能会越界,此时需要使用边界0,因此使用Math.max方法,也就是坐标为Math.max(i-k,0);同样的i+k时也会越界,因此使用min即可。

还有细节问题要处理,就是前缀和定义时需要定义为m+1和n+1,否则计算前缀和时边界问题会很麻烦,但是处理前缀和时对应的原数组的下标应该减一计算;同样的,当计算结果数组时,前缀和下标应该加一计算。

算法代码:

java 复制代码
	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[][] ret = new int[m][n];
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                int x1 = Math.max(i-k,0)+1,y1 = Math.max(j-k,0)+1;
                int x2 = Math.min(i+k,m-1)+1,y2 = Math.min(j+k,n-1)+1;
                ret[i][j] = dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1];
            }
        }
        return ret;
    }
相关推荐
Dream it possible!4 小时前
LeetCode 面试经典 150_多维动态规划_交错字符串(97_C++_中等)
算法·leetcode·面试·动态规划
心抵鹊4 小时前
算法之双指针系列(一)
算法·leetcode
十二4 小时前
【洛谷题解】P1083 [NOIP 2012 提高组] 借教室(二分答案 + 差分)
数据结构·c++·算法·蓝桥杯
小此方5 小时前
[算法实战论:动态规划]回文串问题的dp解法,回文串问题的母题,以及以此为基础的派生题
算法·动态规划
Pniubi6 小时前
力扣560.和为k的子数组
算法·leetcode·职场和发展
D e re k ZH6 小时前
C++ 从零入门(一):基础语法与程序结构
数据结构·c++·算法
Pniubi7 小时前
力扣438.找到字符串中所有字母异位词
算法·leetcode·职场和发展
東隅已逝,桑榆非晚9 小时前
算法6. 双指针法-查找总价格为目标的两个商品
c++·学习·算法
Eyfcom9 小时前
〖物联网终端实战 01〗UWB 室内高精度定位:仓储 AGV 与人员的厘米级轨迹追踪
物联网·算法·物流系统·快递系统·跨境物流快递系统·物联网终端实战
bro_Java66610 小时前
《排序的底层逻辑:比较、交换与分治》
java·算法·排序算法