算法 | 前缀和数组

133 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第27天,点击查看活动详情

前言

本文介绍前缀和数组的基本概念以及两种代码实现。

概念

当要求频繁的对一个数组计算在一定的索引区间的元素之和时,我们可以提前把数据计算好,放到一个数组里,这个数组就被称为前缀和数组。

比如,给定一个数组arr,计算从下标L到下标R之间元素的和,而且要求计算不同下标之间的元素和,极其频繁的调用。

当然简单的方法可以每次都去进行遍历计算,但是如果说调用量次数特别多,上千万次、上亿次,这样每次都去循环遍历判断,效率就差的很多了。

要想提高效率的话,我们可以提前把数组计算出来,放到一个新的数组里,这样每次计算的时候直接去新数组里取就好了,也就是空间换时间的方法,这新的数组就是我们所说的前缀和数组,一般的分为一维前缀和数组、二维前缀和数组。

一维前缀和数组

一维前缀和数组需要准备一个新的数组,把下标0到下标N的数据分别计算出来放入新的数组中,这个新数组help的含义为:

help[i]的值代表arr[0] + arr[1] + ... + arr[i]的元素之和。

image.png

如果我们要计算36之间元素的累加和的话,我们来看一下,help[6]代表arr[0]arr[6]之间元素的累加和,help[2]代表arr[0]arr[2]之间元素的累加和。那么36之间元素的累加和就为help[6]-help[2]

如果要计算06之间的累加和的话,直接取help[6]即可。

代码实现

public class Code07_RangeNum {
    private int[] prefixSum;

    public Code07_RangeNum(int[] arr) {
        prefixSum = new int[arr.length];
        prefixSum[0] = arr[0];
        for (int i = 1; i < arr.length; i++) {
            prefixSum[i] = prefixSum[i - 1] + arr[i];
        }
    }

    public int sumRange(int L, int R) {
        return L == 0 ? prefixSum[R] : prefixSum[R] - prefixSum[L - 1];
    }

    public static void main(String[] args) {
        int[] arr = {3, 5, 2, 7, 1, 8, 13, 9, 12};

        Code07_RangeNum rangeNum = new Code07_RangeNum(arr);
        int sum = rangeNum.sumRange(1, 6);
        System.out.println(sum);
    }
}

二维数组实现思路

我们可以创建一个正方形张表,横边为R、竖边为L,如图。

image.png

因为L<=R,所以对角线下面的数据是不可能存在的,也就是空的。当我们要计算LR上的元素之和时,只需要找到二维数组中对应的数据即可。

在生成这个表示,会生成L * R个元素,因为有一部分是不可能存在的,所以最后需要(L * R) / 2个元素。

代码实现

public class Code08_RangeNum {
    private int[][] prefixSum;

    public Code08_RangeNum(int[] arr) {
        prefixSum = new int[arr.length][arr.length];
        for (int i = 0; i < arr.length; i++) {
            prefixSum[i][i] = arr[i];
            for (int j = i + 1; j < arr.length; j++) {
                prefixSum[i][j] = prefixSum[i][j - 1] + arr[j];
            }
        }
    }

    public int sumRange(int L, int R) {
        return prefixSum[L][R];
    }

    public static void main(String[] args) {
        int[] arr = {3, 5, 2, 7, 1, 8, 13, 9, 12};

        Code08_RangeNum rangeNum = new Code08_RangeNum(arr);
        int sum = rangeNum.sumRange(1, 6);
        System.out.println(sum);
    }
}