【刷题笔记6】前缀和 + 差分数组

0. 应用场景

前缀和的应用场景是,需要对某个区间[i…j]频繁查询累计和,避免每次查询都遍历这个区间。
差分数组的应用场景是,需要对某个区间[i…j]频繁地加或减某一值,避免每次都遍历这个区间。

1. 概念
# 一维前缀和:
vector<int> nums;
vector<int> sums(nums.size() + 1, 0); // 空间比nums大1,前缀和整体右移一位, 这样计算不需要考虑数组越界
for (int i = 0; i < nums.size(); i++) {
	sums[i + 1] = sums[i] + nums[i];
}

解决问题:长度为n的序列,m次询问,每次输入一对数a,b。输出从第a个数到第个数的和;
时间复杂度O(n+m)
计算区间和[l,r] : 
int sum = sums[r + 1] - sums[l]; 
    
# 二维前缀和:
vector<vector<int>> nums;
vector<vector<int>> sums(nums.size() + 1, vector<int>(nums[0].size() + 1, 0));
for (int i = 0; i < nums.size(); i++) {
	for (int j = 0; j < nums[0].size(); j++) {
		sums[i + 1][j + 1] = sums[i][j + 1] + sums[i + 1][j] - sums[i][j] + nums[i][j];
	}
}

解决问题:n行m列的整数矩阵,每次查询给出x1,y1,x2,y2表示一个子矩阵的左上角坐标和右下角坐标,每次输出子矩阵中所有数的和。
计算子矩阵和[x1,y1] --> [x2, y2]:
int sum = sums[x2 + 1][y2 + 1] - sums[x2 + 1][y1] - sums[x1][y2 + 1] + sums[x1][y1];

# 差分数组:
diff[i] = nums[i] - nums[i - 1];
由diff反推原数组, 差分数组的前缀和,即为原数组。

eg, 要对[i,...,j]区间内的元素整体+3,则令diff[i] += 3, diff[j + 1] -= 3即可,复杂度O(1)

2. 前缀和

// 和为K的连续子数组的个数
// 前缀和 
/*
  * sums[i] = sums[i - 1] + nums[i];
  * K = sums[i] - sums[j]; 
  */
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<int, int> mp;
        mp[0] = 1; // 对于一开始的情况,下标0之前没有数据,可以认为前缀和为0,个数为1
        int sums = 0;
        int count = 0;
        for (int i = 0; i < nums.size(); i++) {
            sums += nums[i];
            if (mp[sums - k]) { // if (mp.find(sums - k) != mp.end())
                count += mp[sums - k];
            }
            mp[sums]++; // 遍历至此,前缀和为sums的次数又多了一次
        }
        return count;
    }
};

3. 差分数组

// 航班预订统计:差分数组
class Solution {
public:
    vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {
        vector<int> answer(n);
        vector<int> diff(n + 1); // +1 是为了防止最后一个数计算的时候,栈溢出
        for (int i = 0; i < bookings.size(); i++) {
            diff[bookings[i][0] - 1] += bookings[i][2];
            diff[bookings[i][1] - 1 + 1] -= bookings[i][2];
        }
        answer[0] = diff[0];
        for (int i = 1; i < n; i++) {
            answer[i] = diff[i] + answer[i - 1];
        }
        return answer;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值