LeetCode 前缀和&差分数组

差分数组:

1109. 航班预订统计 https://leetcode-cn.com/problems/corporate-flight-bookings/

1094. 拼车 https://leetcode-cn.com/problems/car-pooling/

算法参考:

https://mp.weixin.qq.com/s?__biz=MzAxODQxMDM0Mw==&mid=2247487011&idx=1&sn=5e2b00c1c736fd7afbf3ed35edc4aeec&chksm=9bd7f02baca0793d569a9633cc14117e708ccc9eb41b7f0add430ea78f22e4f2443f421c6841&scene=21#wechat_redirect

前缀和:

构造前缀和:

最好是前置一个0

    int *preSum = (int *)malloc(sizeof(int) * (numsSize + 1));
    preSum[0] = 0; // 前缀和做一个前置空内容
    // 真正的前缀和是从preSum的1开始的
    for (int i = 0;i < numsSize;++i) {
        preSum[i + 1] = preSum[i] + nums[i];
    }

如果要求任意区间内的元素的和,如 i~ j这个区间里面的元素的和:

    // 上面这么写,就是为了下面循环可以算上nums[0]
    int res = 0;
    for (int left = 0;left < numsSize;++left) {
        for (int right = left;right < numsSize;++right) {
            // 区间和【left .. right】,注意下标
            if (preSum[right + 1] - preSum[left] == k) {
                res++;
            }
        }
    }

 计算区间和为k的所有情况

假设现在有数组a0、a1、a2、a3.....

S0 = 0;

S1 = a0;

S2 = a0 + a1;

S3 = a0 + a1 + a2;

S4 = ...........................;

一般需要配合Hash map一起使用:

前缀和介绍:

https://leetcode-cn.com/problems/subarray-sum-equals-k/solution/bao-li-jie-fa-qian-zhui-he-qian-zhui-he-you-hua-ja/

典型题目:

523. 连续的子数组和 https://leetcode-cn.com/problems/continuous-subarray-sum/

974. 和可被 K 整除的子数组 https://leetcode-cn.com/problems/subarray-sums-divisible-by-k/

560. 和为K的子数组 https://leetcode-cn.com/problems/subarray-sum-equals-k/

以560为例:


// hash map
typedef struct HashMap{
    int preSum; // key 保存前缀和
    int count; // 保存计数值
    UT_hash_handle hh;
}HashMap;
int subarraySum(int* nums, int numsSize, int k){
    if (nums == NULL) {
        return 0;
    }
    // 几乎所有的前缀和使用UT Hash进行优化的时候,均需要使用到一个前置0的提前变量
    HashMap *hashHandle = NULL;
    HashMap *tmp = (HashMap *)malloc(sizeof(HashMap));
    tmp->preSum = 0;
    tmp->count = 1; //这部分必须是1
    HASH_ADD_INT(hashHandle, preSum, tmp);
    int pre = 0;
    int res = 0;
    for (int i = 0;i < numsSize;++i) {
        pre += nums[i]; 
        int value = pre - k; // 顺序不能反了,和很有可能是k或者k的倍数
        HASH_FIND_INT(hashHandle, &value, tmp);
        if (tmp != NULL) {
            res += tmp->count;
        }
        //如果是相同的前缀和,那么需要更新计数值,UT Hash不允许存入preSum数值相同的内容
        // HASH_FIND_INT(hashHandle, &pre, tmp);
        /*
           [1,2,3]
           3
        */
        if (tmp != NULL) {
            tmp->count++; // 要自增
        } else {
            tmp = (HashMap *)malloc(sizeof(HashMap));
            tmp->preSum = pre;
            tmp->count = 1;
            HASH_ADD_INT(hashHandle, preSum, tmp);
        }
    }
    return res;
}

<think>嗯,用户问的是前缀和差分,这两个概念在算法和数据处理中挺常见的。我需要先理清楚它们的定义和应用场景,然后想想怎么用中文解释清楚。 首先,前缀和。记得前缀和应该是一个数组的前缀累积和,对吧?比如有一个数组a,前缀和数组s的第i项是原数组前i项的和。这样的话,当需要快速计算某个区间的和时,比如从l到r,就可以用s[r] - s[l-1]来得到,这样时间复杂度是O(1)的,而预处理前缀和是O(n)。这个点要讲清楚,可能用户需要知道为什么用前缀和,比如优化多次查询的问题。 然后是差分差分数组应该是原数组相邻元素的差,比如数组a的差分数组d,其中d[i] = a[i] - a[i-1],然后d[0]可能是a[0]。这样的话,如果要在原数组的某个区间加上一个常数,只需要在差分数组的两个位置修改,再通过前缀和还原回去。这样可以将区间修改的时间复杂度从O(n)降到O(1)。应用场景比如批量增减,多次区间操作后的结果获取。 可能需要举个例子说明前缀和差分是怎么工作的。比如原数组是[1,3,2,5],前缀和数组就是[1,4,6,11],这样查询区间和的时候比如第2到4项,就是11 -1 =10?不对,原数组索引可能从0开始还是从1开始需要注意。或者例子里的索引可能从1开始计算,所以s[r] - s[l-1],比如l=2,r=4的话是s[4]-s[1]。假设原数组是1-based的话,那例子中的前缀和数组应该是每个位置i保存a[1]到a[i]的和。这时候区间查询就比较直观。 差分的例子,比如要在数组的某个区间加上c,假设原数组是a,差分数组d,那么对a[l..r]加c的话,只需要d[l] +=c,d[r+1] -=c,然后通过求前缀和得到修改后的数组。这里需要确保用户理解差分数组如何通过两次操作完成区间修改,以及如何还原。 可能需要比较前缀和差分的区别,前者用于快速查询区间和,后者用于快速进行区间修改。另外,两者之间其实有互逆的关系,前缀和差分是原数组差分的原数组前缀和。这点可能对高级应用有帮助,但用户可能只需要基础介绍。 还要注意应用场景的举例,比如前缀和在处理子数组和的问题,或者二维前缀和处理矩阵区域和;差分在批量更新,比如日程安排、航班预订统计等问题。LeetCode上有一些题目可以用这两个技巧解决,比如1109航班预订、303区域和检索等。 需要确保语言通俗易懂,避免太多术语,用例子来辅助说明。同时检查LaTeX格式是否正确,行内公式用$...$,独立公式用$$...$$并且单独成段。比如前缀和的定义可以写成$s[i] = a[1] + a[2] + ... + a[i]$,而差分的定义写成$d[i] = a[i] - a[i-1]$。然后例子部分用具体的数组数值来说明。 另外,可能需要指出前缀和差分的预处理步骤的时间复杂度,以及它们如何优化后续的操作。比如前缀和预处理O(n),之后每次查询O(1);差分每次区间修改O(1),最后还原O(n)。 有没有什么常见的错误需要提醒用户?比如在计算前缀和时索引的处理,比如从0开始还是从1开始,导致边界问题。或者差分数组在应用修改时r+1可能越界的情况,需要注意数组大小。不过可能在基础介绍中不需要深入这些细节,但可以提到需要注意边界条件。 总结来说,我需要分步骤介绍前缀和差分,分别给出定义、例子、应用场景以及它们的优势和实现方式。确保结构清晰,语言准确,例子明确,并且符合用户要求的格式,特别是LaTeX的正确使用。</think>好的,我来逐步解释前缀和差分的概念、应用及实现方法: #### 一、前缀和 (Prefix Sum) ##### 1. 基本概念 前缀和是一种**预处理数组数据**的技术,用于快速计算数组任意区间和。定义: - 原数组:$a = [a_1, a_2, ..., a_n]$ - 前缀和数组:$s[i] = a_1 + a_2 + ... + a_i$,其中$s[0]=0$ ##### 2. 计算方式 $$s[i] = \begin{cases} 0 & i=0 \\ s[i-1] + a[i] & 1 \leq i \leq n \end{cases}$$ ##### 3. 应用示例 假设原数组为$[1,3,2,5]$,则前缀和数组为: ``` 索引: 0 1 2 3 4 s[]: 0 1 4 6 11 ``` 计算区间$[2,4]$(即第2到4个元素)的和: $s[4] - s[1] = 11 - 1 = 10$ ##### 4. 核心优势 - **时间复杂度**:预处理$O(n)$,单次查询$O(1)$ - **适用场景**:频繁查询区间和(如LeetCode 303题) #### 二、差分 (Difference Array) ##### 1. 基本概念 差分是一种**优化区间修改操作**的技术。定义: - 原数组:$a = [a_1, a_2, ..., a_n]$ - 差分数组:$d[i] = a[i] - a[i-1]$,其中$d[1] = a[1]$ ##### 2. 核心操作 若要在区间$[l,r]$上增加$c$,只需修改差分数组: ``` d[l] += c d[r+1] -= c # 若r+1超出数组范围则省略 ``` ##### 3. 应用示例 原数组$[1,3,2,5]$对应的差分数组为$[1,2,-1,3]$。若要在区间$[2,3]$上加2: ``` 修改后差分数组: d[2] +=2 → d[2]=1 d[4] -=2 → 超出范围则忽略 最终差分数组: [1,1,-1,3] 通过前缀和还原后的数组: [1,2,1,4] ``` ##### 4. 核心优势 - **时间复杂度**:单次区间修改$O(1)$,最终还原数组$O(n)$ - **适用场景**:航班预订统计(LeetCode 1109题) #### 三、对比总结 | 特性 | 前缀和 | 差分 | |-----------|--------------------|--------------------| | **核心功能** | 快速查询区间和 | 快速进行区间修改 | | **预处理时间** | $O(n)$ | $O(n)$ | | **单次操作时间** | $O(1)$(查询) | $O(1)$(修改) | | **关系** | 差分的逆操作是前缀和 | 前缀和的逆操作是差分 | #### 四、常见应用场景 1. **前缀和** - 子数组和等于k的问题(LeetCode 560) - 二维矩阵区域和检索(LeetCode 304) 2. **差分** - 日程安排冲突检测 - 批量增减操作(如公交车上车人数统计) 如果有具体问题或代码实现需求,可以告诉我具体场景,我会提供更针对性的解释!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值