前缀和算法

这篇具有很好参考价值的文章主要介绍了前缀和算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【模板】前缀和

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

题目链接:前缀和
算法思路

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和
先预处理出来⼀个「前缀和」数组:

  • ⽤ 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]);
            }

        }
    }
}

【模板】二维前缀和

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

题目链接:【模板】二维前缀和
算法思路:

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

代码:
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--;
        }
    }
}

寻找数组的中心下标

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

题目链接:寻找数组的中心下标
算法思路:

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

代码:
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;
    }
}

除自身以外数组的乘积

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

题目链接:除自身以外数组的乘积
算法思路:

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

代码:
class Solution {
    public int[] productExceptSelf(int[] nums) {
        // 题目要求不能使用除法
        // 可以前缀积*后缀积
        int n = nums.length;
        int[] ldp = new int[n+1];
        int[] rdp = new int[n+1];
        // 乘数不能为0
        ldp[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的子数组

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

题目链接:和为k的子数组
算法思路:

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

代码:
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整除的子数组

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

题目链接:和可被K整除的子数组
算法思路:

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和
设 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 。因此,我们仅需⽤⼀个哈希表,⼀边求当前位置的前缀和,⼀边存下之前每⼀种前缀和出现的次数。
前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

代码:
class Solution {
    public int subarraysDivByK(int[] nums, int k) {
        Map<Integer,Integer> hashMap = new HashMap<>();
        // 整个前缀和为0
        hashMap.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;
    }
}

连续数组

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

题目链接:连续数组
算法思想:

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

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

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

代码:
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); // 前缀和为0
        int 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;
    }
}

矩阵区域和

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和

题目链接:https://leetcode.cn/problems/matrix-block-sum/
算法思路:

前缀和算法,算法,算法,模板,一维前缀和,二维前缀和⼆维前缀和的简单应⽤题,关键就是我们在填写结果矩阵的时候,要找到原矩阵对应区域的「左上⻆」以及「右下⻆」的坐标(推荐⼤家画图)左上⻆坐标: 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)
然后将求出来的坐标代⼊到「⼆维前缀和矩阵」的计算公式上即可~(但是要注意下标的映射关系)
文章来源地址https://www.toymoban.com/news/detail-837165.html

代码:
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;
    }
}

到了这里,关于前缀和算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法训练第四十二天|01背包问题 二维 、01背包问题 一维、416. 分割等和子集

    视频链接:https://www.bilibili.com/video/BV1cg411g7Y6/ 参考:https://programmercarl.com/%E8%83%8C%E5%8C%85%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%8001%E8%83%8C%E5%8C%85-1.html 对于面试的话,其实掌握01背包,和完全背包,就够用了,最多可以再来一个多重背包。 而完全背包又是也是01背包稍作变化而来,即:完全

    2024年02月01日
    浏览(46)
  • 前缀和(一维)

    本篇文章我们来介绍一个简单的算法,前缀和。 什么是前缀和? 前缀和是某一个序列的前n项的和,可以理解为数学上的数列的前n项和。 如果 (a) 和 (s) 分别是原数组和前缀和数组,那么应该有如下关系: s[1] = a[1]; 、 s[2] = a[1] + a[2]; 、 s[3] = a[1] + a[2] + a[3]; 如何得到前缀和

    2024年02月08日
    浏览(30)
  • python二维索引转一维索引 & 多维索引转一维索引

    原博客地址:https://blog.csdn.net/qq_42424677/article/details/123011642

    2024年02月11日
    浏览(44)
  • Java——一维数组和二维数组(主要详讲一维数组)

    目录 一维数组 创建,初始化,赋值 注意 一些数组的便捷使用方法 使用 .length得到数组长度 Arrays相关的使用 二维数组 文章某些地方会出现java与c语言的比较 文章内容参考韩顺平老师的课堂笔记 可以先创建再初始化,也可以创建的时候直接初始化。但是,如果选择先创建再

    2024年02月01日
    浏览(51)
  • 前缀和--二维矩阵的前缀和

    原题链接 输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,x2,y2 ,表示一个子矩阵的左上角坐标和右下角坐标。 对于每个询问输出子矩阵中所有数的和。 输入格式 第一行包含三个整数 n,m,q 。 接下来 n 行,每行包含 m 个整数,表示整数矩阵。

    2024年01月16日
    浏览(44)
  • 【PHP】二维数组转一维数组

    在 PHP 中,如果你想将一个二维数组转换为一维数组,你可以使用几种不同的方法。以下是一些常见的方法: array_column() 用于提取数组中的列,最为直接 array_map() 用于对数组中的每个元素应用回调函数,返回的是由回调函数的返回值组成的新数组。 以上任何一种方法都可以

    2024年02月04日
    浏览(65)
  • matlab 二维矩阵变成一维矩阵

    1、一维变二维: https://blog.csdn.net/qq_40584593/article/details/90691276 reshape 2、a(:)即可 https://jingyan.baidu.com/article/d45ad148dc221b29552b80ec.html

    2024年02月11日
    浏览(47)
  • 动态规划—— 01背包问题(一维,二维)

    0-1背包问题是一个经典问题,特别是在算法和动态规划领域。问题是关于一个小偷,他有一个可以携带最大重量的背包,并且他有一组物品,其中每个物品都有自己的价值和重量。小偷希望在不超过背包所能承载的最大重量的情况下,最大化他从这些物品中获得的总价值。问

    2024年02月19日
    浏览(44)
  • 【JavaSE】一维数组和二维数组详解

    欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 目录 一维数组 基本语法 初始化 遍历和打印 数组是引用型变量 基本类型变量与引用类型变量的区别 null 数组传参和返回 总结 二维数组 基本语法 初始化 遍历和打印 数组:可以看成是相同类型元素的

    2024年04月09日
    浏览(44)
  • Python基础 - 将二维列表转换为一维

    在实验中经常会遇到将二维列表(数组)拉平到一维,如将 [[1, 1], [2, 2]] 转换为[1, 1, 2, 2],有以下几种操作方案: 1. 最简单的直接使用循环,如下: 2. 使用itertools.chain(*iterables),能够去除iterable里的内嵌的一层iterable【注意:只能去除一层,多的层数去除不了,具体实例可看下

    2024年02月06日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包