前缀和数组

前言

    本文是基于labuladong算法秘籍的学习总结,是算法总结中的数组与链表篇章,其他篇章以及资料下载在算法学习总结目录中下载,传送门:算法学习总结
  本人是算法菜鸟一枚,通过在学习过程中总结来提升自我,共同进步,有理解不当的地方欢迎大家指正!

一、前缀和理解

前缀和技巧适⽤于快速、频繁地计算⼀个索引区间内的元素之和。前缀和可用于一维及多维数组。

二、原理与代码

2.1 一维数组的前缀和

以典型例题为例,展示一维数组前缀和的构造思路:
给定一个整数数组 nums,求出数组从索引 iji ≤ j)范围内元素的总和,包含 ij两点。
在这里插入图片描述

 class NumArray {
	    private int[] preSum;
	    public NumArray(int[] nums) {
	        preSum = new int[nums.length+1];
	        // preSum[0] = 0; 便于累加
	        for(int i = 1;i<preSum.length;i++){
	            preSum[i] = preSum[i-1]+nums[i-1];
	        }
	    }
    
	    public int sumRange(int left, int right) {
	        return preSum[right+1]-preSum[left];
	    }
    }	

值得注意地方有以下几点:
1、preSum前缀和数组要比原始数组的长度+1
2、构造函数中用于累加的for循环中的i在很多地方也能看到从0开始,若从0开始则如下:

        for(int i = 0;i<preSum.length;i++){
            preSum[i+1] = preSum[i]+nums[i];
        }

2.2 二维数组的前缀和

类似一维数组的前缀和思路,二维数组仅仅在构造前缀和中增加了写固定的计算的步骤,近似于在一维前缀和数组的for循环中使用了不同计算步骤,同样以典型例题讲解原理:
在这里插入图片描述
在这里插入图片描述
sumRegion([2,1,4,3])返回的元素和为8。
对于二维数组,我们同样会构造一个preSum,只是在for循环中构造的计算步骤稍区别于一维,具体计算步骤思路如下:
在这里插入图片描述
想计算红⾊的这个⼦矩阵的元素之和,可以⽤绿⾊矩阵减去蓝⾊矩阵减去橙⾊矩阵最后加上粉⾊矩阵,⽽绿蓝橙粉这四个矩阵有⼀个共同的特点,左上⻆是 (0, 0) 原点。
代码如下:

class NumMatrix {
	// preSum[i][j] 记录矩阵 [0, 0, i, j] 的元素和
	private int[][] preSum;
	public NumMatrix(int[][] matrix) {
		int m = matrix.length, n = matrix[0].length;
		if (m == 0 || n == 0) return;
		// 构造前缀和矩阵
		preSum = new int[m + 1][n + 1];
		for (int i = 1; i <= m; i++) {
			for (int j = 1; j <= n; j++) {
				// 计算每个矩阵 [0, 0, i, j] 的元素和
				preSum[i][j] = preSum[i-1][j] + preSum[i][j-1] + matrix[i- 1][j - 1] - preSum[i-1][j-1];
			}
		}
	}
	// 计算⼦矩阵 [x1, y1, x2, y2] 的元素和
	public int sumRegion(int x1, int y1, int x2, int y2) {
		// ⽬标矩阵之和由四个相邻矩阵运算获得
		return preSum[x2+1][y2+1] - preSum[x1][y2+1] - preSum[x2+1][y1] + preSum[x1][y1];
	}
}

三、典型例题

基于以上的知识可以解决Leetcode中的如下题目:
303. 区域和检索 - 数组不可变(中等)
304. ⼆维区域和检索 - 矩阵不可变(中等)
560. 和为K的⼦数组(中等)

四、补充知识

这里将对本节中涉及的一些基础知识做一些补充和拓展

1、构造函数

在前缀和的算法题中,题目经常会要求我们实现一个前缀和的类,这个类将被如下方式调用:

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray obj = new NumArray(nums);
 * int param_1 = obj.sumRange(left,right);
 */

因此,要记得实现一个有参构造函数,因此在此拓展一下构造函数的相关知识:
1)每个方法必然有一个构造方法,有参或无参,若没有提供有参,则会自动构造一个无参。
2)创建一个子类对象的实例的时候,必先调用父类的无参构造函数(默认构造函数),除非子类显示指定super。若父类的无参为private或在有有参情况下,没有显示定义无参,或者子类没有显示指定继承父类构造方法,编译会报错。
因此,解决方法一般是,在子类super指定,或者在父类显示添加无参构造方法。

2、修饰符范围

上面的构造函数中提到了修饰符,因此,补充一下修饰符的基础。
在这里插入图片描述
还有final、和abstract修饰符:
final修饰的类不能被继承,没有子类。
abstract修饰的类不能被实例化,必须被子类继承。类只要有一个抽象方法就必定是抽象类,但抽象类不一定要有抽象方法。

3、二维数组

二维数组可以理解为是一维数组,只不过他的各处的元素是特殊元素—–一维数组
a[0][0] a[0][1] a[0][2] a[0][3]
a[1][0] a[1][1] a[1][2] a[1][3]
a[2][0] a[2][1] a[2][2] a[2][3]

//定义一个整型数组:3行4列int a[][] = new int[3][4];
//获取行数---3行int lenY = a.length;
//获取列数---4列int lenX = a[0].length;
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值