差分数组/前缀和

文章目录

  • 1094. 拼车
  • 1109. 航班预定统计
  • 303. 区域和检索 - 数组不可变
  • 560. 和为K的子数组
  • 523. 连续的子数组的和

1094. 拼车

差分数组/前缀和_第1张图片

class Solution {
    public boolean carPooling(int[][] trips, int capacity) {
        int[] diff = new int[1001]; // 记录每个站点改变的人数,比如增加几个人,减少几个人
        for(int[] trip : trips){
            int cnt = trip[0];
            int from = trip[1];
            int to = trip[2];
            diff[from] += cnt;
            diff[to] -= cnt;
        }
        int num = 0; // 记录每个站点车上的人数
        for(int i = 0; i < 1001; i++){
            num += diff[i];
            if(num > capacity){
                return false;
            }
        }
        return true;
    }
}

1109. 航班预定统计

差分数组/前缀和_第2张图片
差分数组/前缀和_第3张图片
原数组后一位元素 - 前一位元素,得到的就是差分数组

通过差分数组求前缀和,可以得到原数组。对原数组区间[l, r]增加inc,相当于对差分数组的diff[l] += inc,diff[r + 1] -= inc

class Solution {
    public int[] corpFlightBookings(int[][] bookings, int n) {
        int[] diff = new int[n];
        int m = bookings.length;
        for(int i = 0; i < m; i++){
            int l = bookings[i][0];
            int r = bookings[i][1];
            int cnt = bookings[i][2];
            diff[l - 1] += cnt;
            if(r < n) diff[r] -= cnt;
        }
        for(int i = 1; i < n; i++){
            diff[i] += diff[i - 1];
        }
        return diff;
    }
}

303. 区域和检索 - 数组不可变

差分数组/前缀和_第4张图片

我们对原数组的[l, r]区间求和,可以转换为前缀和数组的差:pre[r] - pre[l - 1]

对前缀和求差分数组,可以得到原数组
对差分数组求前缀和,也可以得到原数组

class NumArray {
    int n;
    int[] pre;

    public NumArray(int[] nums) {
        n = nums.length;
        pre = new int[n];
        pre[0] = nums[0];
        for(int i = 1; i < n; i++){
            pre[i] = pre[i - 1] + nums[i];
        }
    }
    
    public int sumRange(int l, int r) {
        if(l == 0) return pre[r];
        return pre[r] - pre[l - 1];
    }
}

560. 和为K的子数组

差分数组/前缀和_第5张图片

class Solution {
    public int subarraySum(int[] nums, int k) {
        // 也就是前缀和数组两数之差为k
        int n = nums.length;
        int[] pre = new int[n];
        pre[0] = nums[0];
        for(int i = 1; i < n; i++){
            pre[i] = pre[i - 1] + nums[i];
        }
        int ans = 0;
        // presum, cnt
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(0, 1);   // 表示pre[i]就是k,即[0,i]和为k
        for(int i = 0; i < n; i++){
            if(map.containsKey(pre[i] - k)){
                ans += map.get(pre[i] - k);
            }
            map.put(pre[i], map.getOrDefault(pre[i], 0) + 1);
        }
        return ans;
    }
}

523. 连续的子数组的和

差分数组/前缀和_第6张图片
若区间和为k的整数倍,说明前缀和数组两个元素的差应该是k的整数倍,也即每个元素%k后,余数相同。余数相同,则表示两数之差是k的整数倍

class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        int n = nums.length;
        int[] pre = new int[n];
        pre[0] = nums[0];
        for(int i = 1; i < n; i++){
            pre[i] = pre[i - 1] + nums[i];
        }
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(0, -2); // 如果前缀和数组 某个数 % k 的余数就是0,说明[0, i]区间的和就是k的整数倍,下标设置为-2
        for(int i = 0; i < n; i++){
            if(map.containsKey(pre[i] % k)){
                if(i > 0 && i - map.get(pre[i] % k) >= 2){
                    // 同余的同时,还需要满足i > 0,因为i == 0时数组长度是1
                    return true;
                }
            }else{
                // 余数已经出现过了,就不再更新了
                map.put(pre[i] % k, i);
            }
        }
        return false;
    }
}

总结:

差分数组主要是把对原数组某个区间的操作,转换为对两端点的操作,[l, r] -> l, r + 1

前缀和数组可以把对原数组[l, r]区间求和的操作,转化为两端点的差pre[r] - pre[l - 1]

你可能感兴趣的:(java,算法,数据结构)