前缀和算法详解:原理、实现与优化策略

目录

一、算法定义

二、算法原理

三、C++实现代码

四、关键实现细节

五、复杂度分析

六、典型应用场景

七、算法特性对比

八、注意事项

九、测试用例

十、算法扩展


 

一、算法定义

        前缀和(Prefix Sum)是一种预处理技术,通过构建一个辅助数组存储原数组前n项的和,将区间和的查询时间复杂度从O(n)优化到O(1)。其核心思想是空间换时间,适用于静态数组的频繁区间求和场景。

二、算法原理

  1. preSum数组构建

    • preSum[0] = 0(边界条件)

    • preSum[i] = Σarr[0..i-1](i≥1)

    • 数学公式:preSum[i] = preSum[i-1] + arr[i-1]

  2. 区间和计算

    • 区间[i,j]的和 = preSum[j+1] - preSum[i]

    • 索引对齐原理:preSum数组比原数组长度+1

三、C++实现代码

#include <vector>
using namespace std;

class PrefixSum {
private:
    vector<int> preSum;

public:
    // 预处理构造函数 O(n)
    PrefixSum(const vector<int>& nums) {
        int n = nums.size();
        preSum.resize(n + 1, 0);
        for (int i = 1; i <= n; ++i) {
            preSum[i] = preSum[i-1] + nums[i-1];
        }
    }

    // 查询区间和 O(1)
    int query(int left, int right) {
        return preSum[right + 1] - preSum[left];
    }

    // 获取前缀和数组(调试用)
    vector<int> getPrefixArray() {
        return preSum;
    }
};

四、关键实现细节

  1. 索引偏移处理

    • 原数组索引i对应preSum[i+1]

    • 示例:nums[0] → preSum[1]

  2. 防止溢出

    • 对于大数场景应使用long类型:

      vector<long> preSum(n+1, 0L);
  3. 空数组处理

    • 构造函数需检查nums是否为空:

      if (nums.empty()) throw invalid_argument("Empty input");
  4. 区间合法性校验

    if (left < 0 || right >= preSum.size()-1 || left > right)
        throw out_of_range("Invalid range");

     

五、复杂度分析

操作时间复杂度空间复杂度
构造函数O(n)O(n)
query()O(1)O(1)

六、典型应用场景

  1. 高频区间求和

    // 频繁调用示例
    vector<int> nums {1,3,5,7,9};
    PrefixSum ps(nums);
    cout << ps.query(1,3); // 3+5+7=15
  2. 二维前缀和扩展

    // 矩阵版本预处理
    preSum[i][j] = preSum[i-1][j] + preSum[i][j-1] 
                 - preSum[i-1][j-1] + matrix[i-1][j-1];
  3. 哈希表结合应用

    // 求子数组和为k的个数
    unordered_map<int, int> countMap;
    countMap[0] = 1;
    for (int sum : preSum) {
        if (countMap.count(sum - k)) 
            res += countMap[sum - k];
        countMap[sum]++;
    }

     

七、算法特性对比

方法预处理时间查询时间适用场景
暴力法O(1)O(n)低频查询
前缀和O(n)O(1)高频区间查询
线段树O(n)O(logn)动态数据+区间操作

八、注意事项

  1. 数据修改限制

    • 原数组必须为静态数据(构建后不可修改)

    • 动态修改需使用树状数组等结构

  2. 内存优化

    • 原地计算法(节省空间):

      for (int i = 1; i < nums.size(); ++i)
          nums[i] += nums[i-1];
  3. 浮点数精度

    • 对于浮点型数据需注意累积误差

九、测试用例

void test() {
    vector<int> testCase = {2, -1, 3, 5};
    PrefixSum ps(testCase);
    
    assert(ps.query(0, 0) == 2);
    assert(ps.query(1, 3) == (-1)+3+5);
    assert(ps.query(0, 3) == 2+(-1)+3+5);
}

十、算法扩展

  1. 差分数组

    • 前缀和的逆运算

    • 适用于区间更新操作

  2. 环形数组处理

    // 处理环形数组区间和
    if (left <= right) 
        return preSum[right+1] - preSum[left];
    else 
        return preSum.back() - (preSum[left] - preSum[right+1]);
  3. 权重前缀和

    // 带权重的前缀和
    preSum[i] = preSum[i-1] + nums[i-1] * weight[i-1];

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值