前缀和(一)

本文首发自本人微信公众号:今天你A了吗。每日算法讲解,面试题,冲刺BAT大厂。微信扫码关注吧:
在这里插入图片描述

前缀和(一)仅涉及一维前缀和。前缀和(二)涉及二维前缀和。

一、介绍:什么是前缀和

假设我们有一个字符串ABCDE,什么是这个单词的前缀,A、AB、ABC、ABCD、ABCDE就是这个单词的前缀,就是从第一个字母开始,依次往后拼接。E、ED、EDC、EDCB、EDCBA被称为这个单词的后缀。

那么对于一个数组的前缀,例如数组a = [1,2,3,4,5],我们维护一个由前缀的和组成的数组sum,sum[i]表示数组中a[0]~ a[i] 的和。
sum[0] = a[0]
sum[1] = a[0] + a[1]
sum[2] = a[0] + a[1] + a[2]
sum[3] = a[0] + a[1] + a[2] + a[3]
sum[4] = a[0] + a[1] + a[2] + a[3] + a[4]
sum数组就被称为前缀和数组。

二、前缀和的作用

前缀和的最主要目的就是求子数组的和的大小。例如元素a[1]a[3]的和。
a[1] + a[2] + a[3] = sum[3] - sum[0]
注意:这里sum中的i表示的是前i个数的和,不是下标,因为题目中需要用到前0个数的和。

三、应用

下面通过一个例子说明一下前缀和的作用。
LeetCode 560.和为k的子数组:https://leetcode-cn.com/problems/subarray-sum-equals-k/
题意:给定一个整数数组和一个整数 k你需要找到该数组中和为 k的连续的子数组的个数。

思路1: 如果只使用for循环枚举子数组,并且求和,时间复杂度为O(n3)使用前缀和。使用前缀和:每次求出子数组的和,与k进行比较,时间复杂度O(n2)。

class Solution {
    public int subarraySum(int[] nums, int k) {

        int n = nums.length;
        int count = 0;
        int[] sum = new int[n]; //前缀和数组

        //得到前缀和数组
        sum[0] = nums[0];
        for(int i = 1; i < n; i++){
            sum[i] = sum[i-1] + nums[i];
        }


        //列举子数组
        for(int i = 0; i < n; i++){
            for(int j = -1; j < i; j++){ //这里从-1开始下面有解释
                int sumOfSub;
                if(j < 0)
                    sumOfSub = sum[i];
                else
                    sumOfSub = sum[i] - sum[j];
                
                if(sumOfSub == k)
                    count++;
            }
        }
        return count;

    }
}

解释:关于题目中的j = -1
a[1]+a[2] = sum[2]-sum[0]
a[0]+a[1]+a[2] = sum[2] //这里不需要减去任何东西,所以代码中用一个-1来表示这种特殊情况。

思路二: 使用Map + 前缀和进行优化
在上一个思路中,我们要求一个结尾下标为i的子数组的和是否为k,就需要对j-1遍历到i-1,来找到是存在否sum[i] - k = sum[j]。那么我们只要用map<前i个元素的前缀和,出现的次数>把i之前的前缀和的值记录下来,判断map中是否包含sum[i] - k即可。时间复杂度O(n)

class Solution {
    public int subarraySum(int[] nums, int k) {

        int n = nums.length;
        int count = 0;
        int[] sum = new int[n]; //前缀和数组
        Map<Integer, Integer> map = new HashMap<>();

        map.put(0, 1); //放入没有前缀和这种特殊情况,见思路一的解释,0这个值出现了1次

        //得到前缀和数组
        sum[0] = nums[0];
        for(int i = 1; i < n; i++){
            sum[i] = sum[i-1] + nums[i];
        }


        //列举子数组
        for(int i = 0; i < n; i++){
           if(map.containsKey(sum[i]-k))
                count += map.get(sum[i]-k) ;
            map.put(sum[i], map.getOrDefault(sum[i], 0) + 1);

        }
        return count;

    }
}

四、相关题目

  1. 有多少小于当前数字的数字 https://leetcode-cn.com/problems/how-many-numbers-are-smaller-than-the-current-number/
  2. 统计「优美子数组」 https://leetcode-cn.com/problems/count-number-of-nice-subarrays/
### 前缀和的概念 前缀和种通过预先计算数组的部分和来加速区间求和操作的技术。其核心思想是构建个新的数组 `PrefixSum`,其中每个元素表示原数组从起始位置到当前位置的所有元素之和[^4]。 定义为: \[ \text{PrefixSum}[i] = A[0] + A[1] + ... + A[i-1] \] 这意味着可以通过简单的减法运算快速获取任意子区间的和,而无需重复遍历原始数组中的元素。 --- ### 前缀和的实现方法 以下是基于 Python 的前缀和算法实现: ```python def calculate_prefix_sum(arr): n = len(arr) prefix_sum = [0] * (n + 1) # 创建个长度为 n+1 的列表用于存储前缀和 for i in range(1, n + 1): # 从索引 1 开始填充前缀和数组 prefix_sum[i] = prefix_sum[i - 1] + arr[i - 1] return prefix_sum # 使用示例 arr = [3, 1, -2, 8, 7] prefix_sum = calculate_prefix_sum(arr) # 查询某个区间 [l, r] 的和 def query_range_sum(prefix_sum, l, r): return prefix_sum[r + 1] - prefix_sum[l] # 测试查询功能 print(query_range_sum(prefix_sum, 1, 3)) # 输出应为 1 + (-2) + 8 = 7 ``` 在此代码中,`calculate_prefix_sum` 函数负责生成前缀和数组,而 `query_range_sum` 则利用该数组高效地计算指定区间的和。 --- ### 计算方式详解 假设有个数组 \( A = [a_0, a_1, ..., a_{n-1}] \),那么对应的前缀和数组 \( PrefixSum \) 可以按照以下公式逐项计算得出: \[ \text{PrefixSum}[i] = \text{PrefixSum}[i-1] + A[i-1], \quad \forall i > 0 \] 初始条件为 \( \text{PrefixSum}[0] = 0 \)[^4]。 旦完成前缀和数组的预处理,任何区间 \([l, r]\) 的和都可以通过次减法操作获得: \[ \text{RangeSum}(l, r) = \text{PrefixSum}[r+1] - \text{PrefixSum}[l] \] 这种方法的时间复杂度为 O(n)(预处理阶段)以及 O(1)(每次查询阶段),显著优于直接暴力求解的方法。 --- ### 示例分析 考虑输入数组 \( A = [3, 1, -2, 8, 7] \): | 原始数组 | 3 | 1 | -2 | 8 | 7 | |----------|-----|-----|------|-------|-------| | 下标 | 0 | 1 | 2 | 3 | 4 | 经过前缀和计算后得到的结果如下表所示: | 前缀和下标 | 0 | 1 | 2 | 3 | 4 | 5 | |------------|-----|-----|-----|------|------|------| | 前缀和值 | 0 | 3 | 4 | 2 | 10 | 17 | 如果要查询区间 `[1, 3]` 的和,则可以直接使用公式: \[ \text{RangeSum}(1, 3) = \text{PrefixSum}[4] - \text{PrefixSum}[1] = 10 - 3 = 7 \] 这表明区间 `[1, 3]` 中的元素总和为 7。 ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值