前缀和与差分数组

本文介绍了前缀和与差分数组的概念及其在LeetCode中解决数组和矩阵区域和检索问题的高效方法。通过使用前缀和,可以在O(1)时间内完成区间和的查询,而差分数组则能在常数时间内完成区间元素的增减操作。文章列举了多个例子,如303.区域和检索、560.和为K的子数组等,展示了如何利用这两种数据结构优化算法,降低时间复杂度。
摘要由CSDN通过智能技术生成

前缀和与差分数组

基本概念

  • 前缀和

我们有一个数组,如果我们要知道区间 [L, R] 的和,通常的做法是遍历一遍;这种做法在访问次数比较少的情况下或许可以,但是当我们需要多次访问时,若访问 m 次,时间复杂度为 O(m * n)。
而利用前缀和(空间换时间的方式),构造一个数组 sum,数组元素记录该索引之前(包括该索引)的数组元素之和。我们只需要通过 sum[R] - sum[L - 1] 就能求出指定范围之和。若访问 m 次,时间复杂度为 O(m)。

for (int i = 1; i <= n; i++) {
    sum[i] = sum[i - 1] + arr[i];
}
  • 差分数组

如果我们需要对数组的一个区间进行频繁增减,通常的做法是在遍历的同时进行增减,这种做法的时间复杂度为 O(N)。而引入差分数组,我们只需要的时间复杂度为 O(1)。

for (int i = 1; i < n; i++) {
    diff[i] = nums[i] - nums[i - 1];
}

Leetcode 例题

303.区域和检索-数组不可变
思路解析:
我们自己定义一个数组 sums,将 sums[0] 设为 0,sums[i] 表示从 num[0] 到 num[i - 1] 累加和。

class NumArray {
public:
    vector<int> sums;
    NumArray(vector<int>& nums) {
        int len = nums.size();
        sums.resize(len + 1);
        for (int i = 1; i <= len; i++) {
            sums[i] = sums[i - 1] + nums[i - 1];
        }
    }
    
    int sumRange(int left, int right) {
        return sums[right + 1] - sums[left];
    }
};

560.和为K的子数组

思路解析:

pre[j - 1] == pre[i] - k

我们考虑以 i 结尾的和为 k 的连续子数组个数时只要统计有多少个前缀和为 pre[i] - k 的 pre[j] 即可。我们建立哈希表 mp,以和为键,出现次数为对应的值,记录 pre[i] 出现的次数,从左往右边更新 mp 边计算答案,那么以 i 结尾的答案 mp[pre[i]−k] 即可在 O(1) 时间内得到。最后的答案即为所有下标结尾的和为 k 的子数组个数之和。

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<int, int> hash;
        int sum = 0, cnt = 0;
        hash[0] = 1;
        for (int i = 0; i < nums.size(); i++) {
            sum += nums[i];
            if (hash[sum - k]) cnt += hash[sum - k];
            hash[sum]++;
        }
        return cnt;
    }
};

304.二维区域和检索-矩阵不可变

思路解析:
负雪明烛题解

class NumMatrix {
public:
    vector<vector<int>>preSum;
    NumMatrix(vector<vector<int>>& matrix) {
        if(matrix.empty()){
            return ;
        }
        int n=matrix.size();
        int m=matrix[0].size();
        preSum.resize(n+1);
        for(int i=0;i<n+1;i++){
            preSum[i].resize(m+1);
        }
        if (n> 0) {
            for (int i = 0; i <n; i++) {
                for (int j = 0; j <m; j++) {
                    preSum[i+1][j+1] = preSum[i][j+1] + preSum[i+1][j] - preSum[i][j] + matrix[i][j];
                }
            }
        }
    }
    int sumRegion(int row1, int col1, int row2, int col2) {
        return preSum[row2 + 1][col2 + 1] - preSum[row2 + 1][col1] - preSum[row1][col2 + 1] + preSum[row1][col1];
    }
};

1094.拼车

class Solution {
public:
    bool carPooling(vector<vector<int>>& trips, int capacity) {
        if (trips.empty()) {
            return false;
        }
        const int MAX_PASSNUM = 1001;
        vector<int> changeNum(MAX_PASSNUM, 0);
        for (int i = 0; i < trips.size(); i++) {
            changeNum[trips[i][1]] += trips[i][0];
            changeNum[trips[i][2]] -= trips[i][0];
        }
        int sum = 0;
        for (int j = 0; j < changeNum.size(); j++) {
            sum += changeNum[j];
            if (sum > capacity) {
                return false;
            }
        }
        return true;
    }
};

1109.航班预定统计

class Solution {
public:
    vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {

        vector<int> relust(n+1, 0); 
        int diff = 0;
        for(int i = 0; i < bookings.size(); i++){
            int k = bookings[i][0]-1;                
            diff = bookings[i][2];
            relust[k] += diff;
            int j = bookings[i][1];
            relust[j] -= diff;
            }
            

        for(int i = 1; i < n; i++){
            relust[i] += relust[i-1];
        }

        relust.pop_back();
        return relust;
    }
};

原文地址:http://www.bcysf.cn/index.php/archives/6/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值