基本数据结构---前缀和数组

一、应用场景?

前缀和主要适用的场景是原始数组不会被修改的情况下,频繁查询某个区间的累加和.prefix[i+1] 就代表着 nums[0…i] 所有元素的累加和, 如果我们想求区间 nums[i…j] 的累加和, 只要 计算 prefix[j+1] - prefix[i] 即可, ⽽不需要遍历整个区间求和。

在这里插入图片描述

二、模板

		 preSum = new int[nums.length + 1]; 
		 //preSum[i]数组维护nums前i个和,即[0,...,i-1],preSum[0]=0(方便计算);
        for (int i = 0; i < nums.length; i++) {       //e.g.  nums[0,1,2,3]
            preSum[i + 1] = preSum[i] + nums[i];    //   preSum[0,0,1,3,6]
            System.out.println(preSum[i]);
        }

三、实践

303. 区域和检索 - 数组不可变

在这里插入图片描述
在这里插入图片描述

public class NumArray {
    private int[] preSUm;

    public NumArray(int[] nums) {
        fun(nums);
    }

    //构造前缀和数组
    public void fun(int[] nums) {
        preSUm = new int[nums.length + 1];//preSum[i]代表了nums中前i个数之和,即nums[0...i-1]的和
        //preSum[0]=0为了方便后续计算
        for (int i = 1; i <= nums.length; i++) {
            preSUm[i] = preSUm[i - 1] + nums[i - 1];
        }
    }

    public int sumRange(int left, int right) {
        return preSUm[right + 1] - preSUm[left];
    }
}

304. 二维区域和检索 - 矩阵不可变

在这里插入图片描述
在这里插入图片描述

计算红⾊的这个⼦矩阵的元素之和, 可以⽤绿⾊矩阵减去蓝⾊矩阵减去橙⾊矩阵最后加上粉⾊矩 阵, ⽽绿蓝橙粉这四个矩阵有⼀个共同的特点, 就是左上⻆就是 (0, 0) 原点。

可以维护⼀个⼆维 preSum 数组, 专⻔记录以原点为顶点的矩阵的元素之和, 就可以⽤⼏次加减运 算算出任何⼀个⼦矩阵的元素和:

public class NumMatrix {
    private int[][] preSum;

    public NumMatrix(int[][] matrix) {
        fun(matrix);
    }

    //构造前缀和
    public void fun(int[][] matrix) {
        int n = matrix.length, m = matrix[0].length;
        preSum = new int[n + 1][m + 1];//preSum[i][j]代表matrix[0...i-1][0...j-1]之和
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] - preSum[i - 1][j - 1] + matrix[i - 1][j - 1];
            }
        }
    }

    public int sumRegion(int row1, int col1, int row2, int col2) {
        return preSum[row2 + 1][col2 + 1] - preSum[row1][col2 + 1] - preSum[row2 + 1][col1] + preSum[row1][col1];
    }
}

560. 和为 K 的子数组

在这里插入图片描述

//方法一
       public int subarraySum(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int[] preSum = new int[nums.length + 1];//1、构造前缀和
        for (int i = 1; i <= nums.length; i++) {
            preSum[i] = preSum[i - 1] + nums[i - 1];
        }
        int res = 0;
        for (int i = 1; i <= nums.length; i++) {//2、穷举所有元素
            for (int j = 0; j < i; j++) {
                if (preSum[i] - preSum[j] == k) {//3、nums[j,...,i-1]的和
                    res++;
                }
            }
        }
        return res;
    }

在这里插入图片描述
方法2(对方法一的优化)

	public int subarraySum(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(0, 1);
        int res = 0, preSum = 0;
        for (int i = 0; i < nums.length; i++) {
            preSum += nums[i];//记录前缀和
            if (map.containsKey(preSum - k)) {//有满足条件的前缀和
                res += map.get(preSum - k);
            }
            map.put(preSum, map.getOrDefault(preSum, 0) + 1);//更新满足提议的前缀和个数
        }
        return res;
    }

总结

前缀和数组主要适⽤的场景是原始数组不会被修改的情况下,频繁查询某个区间的累加和。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值