[LeetCode]-Prefix Sum & Difference

prefix and

For one-dimensional arrays of integers, the prefix sum algorithm is the construction(The construction mentioned here does not mean that such an array must be constructed when solving the problem. The explanation here is just to understand the idea of ​​prefix sum. After understanding the idea, you can use this algorithm with your own ideas)Create a prefix sum array sum that is the same length as the original array nums, and in the prefix sum array, sum[i] represents the sum of the elements [0,i] in the original array. For the problem of finding the interval sum of a one-dimensional array
Prefixes and example diagrams
, You can use prefix sum to solve the problem, especially if you want to find the interval sum for the same one-dimensional array multiple times, you only need to traverse the array once with a complexity of O(n) to generate the prefix sum array, and then you only need to use For the prefix sum array, you can get the answer by subtracting the prefix sum corresponding to the left boundary of the interval from the prefix sum corresponding to the right boundary of the interval. The complexity is O(1)

303.Region and retrieval

class NumArray {
    
    
    private int[] sum;
    public NumArray(int[] nums) {
    
    
        int len = nums.length;
        if(len == 0) sum = null;
        else{
    
    
        	//开始生成前缀和数组
            sum = new int[len];
            sum[0] = nums[0];
            for(int i = 1;i < len;i++){
    
    
            	//生成前缀和数组的递推公式
                sum[i] = sum[i - 1] + nums[i];
            }
        }
    }
    public int sumRange(int left, int right) {
    
    
        if(sum == null) return 0;
        if(left == 0) return sum[right];
        return sum[right] - sum[left - 1];
    }
}

304. Two-dimensional region and retrieval

This question is equivalent to the algorithm for prefix sum of two-dimensional arrays. Similar to the method of one-dimensional array, construct a two-dimensional prefix sum array sum whose rows and columns are equal to the original two-dimensional array, where sum[i][j] means is the sum of all elements num[row][col] in the original array for 0 <= row <= i and 0 <= col <= j, that is, [0,i] is the length, [0,j] is The sum of all the elements in a wide rectangle.
So how to find the sum of the elements in the rectangle in the range from (row1,col1) to (row2,col2): It
2D area and retrieval example diagram
can be seen that the rectangle in the range from (row1,col1) to (row2,col2) is The large rectangle from (0,0) to (row2,col2) minus the small rectangle of the red area and the blue area in the picture plus the area where the red area and the blue area are subtracted twice, that is, sum[ row2][col2] - sum[row1 - 1][col2] - sum[row2][col1 - 1] + sum[row1 - 1][col1 - 1]

class NumMatrix {
    
    
    private int[][] sum;
    public NumMatrix(int[][] matrix) {
    
    
        int m = matrix.length;
        int n = matrix[0].length;
        sum = new int[m][n];
        sum[0][0] = matrix[0][0];
        //矩形的第一行跟第一列的前缀和计算方法与其它位置的不同
        for(int i = 1;i < n;i++) sum[0][i] = sum[0][i - 1] + matrix[0][i];
        for(int i = 1;i < m;i++) sum[i][0] = sum[i - 1][0] + matrix[i][0];
        for(int i = 1;i < m;i++){
    
    
            for(int j = 1;j < n;j++){
    
    
                sum[i][j] = matrix[i][j] + sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1];
            }
        } 
    }
    public int sumRegion(int row1, int col1, int row2, int col2) {
    
    
        if(row1 == 0 && col1 == 0) return sum[row2][col2];
        if(row1 == 0) return sum[row2][col2] - sum[row2][col1 - 1];
        if(col1 == 0) return sum[row2][col2] - sum[row1 - 1][col2];
        //下面这个计算公式是应对于普通情况的,可以发现不适用的情况是 col1 == 0 或者 row1 == 0,对应的就有三种输入情况
        //分别是row1跟col1都为0,row1为0col1不为0.row1不为0col1为0 
        return sum[row2][col2] - sum[row1 - 1][col2] - sum[row2][col1 - 1] + sum[row1 - 1][col1 - 1];
    }
}

560. Subarray whose sum is K

For a one-dimensional array, its consecutive subarrays either start from element 0 (contains element 0); or they do not start from element 0. For the former, using
prefix and solution is to find the value in the prefix sum array sum. How many elements are there for k?
For the latter, assuming that the sum of the elements in the subarray [i,j] is k, 0 < i <= j, then correspondingly, the sum of the elements of the subarray [0,i) is sum[ j] - k, which is sum[i] in the prefix and array. Specifically for this question, you can traverse the entire array, then record the sum of the currently traversed elements, and use map to record the number of occurrences of each sum, and then determine the value of the key sum - k in the map, that is How many of the prefix sums that have been traversed are sum - k

public int subarraySum(int[] nums, int k) {
    
    
    int len = nums.length;
    Map<Integer,Integer> map = new HashMap<>();
    //map记录数组中,前缀和为某个值的前缀子数组的个数
    //初始设置一个前缀和为0时有一个子数组,这一个表示的是一个数组元素都不选时长度为0的子数组
    //这样后续遇到刚好有前缀和为k的时候,按照上面说的第二种情况来处理即可
    map.put(0,1);
    int sum = 0;
    int res = 0;
    for(int i = 0;i < len;i++){
    
    
    	//这里就不用真的去构造一个前缀和数组,只需遍历计算出每一个前缀和直接使用即可
        sum += nums[i];
        int key = sum - k;
        if(map.containsKey(key)){
    
    
            res += map.get(key);
        }
        map.put(sum,map.getOrDefault(sum,0) + 1);
    }
    return res;
}

1442. The number of triples that form two exclusive or equal arrays (one question per day)

The following explanation comes from the official solution of LeetCode.
Official solution
It should be noted that s[0] represents 0. To satisfy a = b, s[i] = s[k + 1] is required (according to the condition of 0 <= i < j <= k < arr.length, we can get i < k), regardless of j, so j It only needs to be within the range of (i,k]. That is to say, every time a group of s[i] = s[k + 1] is found, there are k - i corresponding triples. According to this, we can get code below

public int countTriplets(int[] arr) {
    
    
    int n = arr.length;
    int[] s = new int [n + 1];
    for(int i = 1;i <= n;i++){
    
    
        s[i] = arr[i - 1] ^ s[i - 1];
    }
    int ans = 0;
    for(int i = 0;i < n;i++){
    
    
        for(int k = i + 1;k < n;k++){
    
    
            if(s[i] == s[k + 1]){
    
    
                ans += k - i; //k + 1 - i - 1
            }
        }
    }
    return ans;
}

The above code can be said to be the optimal solution
. Let us start from the perspective of k: for each k, we are actually looking for how many s[i] = s[k + 1] there are in front of it. Every time we find one, ans Just add k, and then subtract i. That is to say, for each subscript k, as long as you know how many subscripts i correspond to s[i] = s[k + 1] in front of k and the sum of all these subscripts i, you can find each k’s contribution to the final answer ans. The implementation idea is to use hash to record the number of occurrences of the same value in s[i] that has been traversed and the sum of all corresponding subscripts i, so that the complexity of O(n) can be achieved:

public int countTriplets(int[] arr) {
    
    
    int n = arr.length;
    int[] s = new int [n + 1];
    //count即记录每个前缀和及其出现的次数
    HashMap<Integer,Integer> count = new HashMap<>();
    //indexSum记录每个相等的前缀和s[i]的下标i的总和
    HashMap<Integer,Integer> indexSum = new HashMap<>();
    int ans = 0;
    //k从1开始,所以把s[0]对应的数据先放入哈希表
    count.put(0,1);
    indexSum.put(0,0);
    for(int k = 1;k <= n;k++){
    
    
        s[k] = arr[k - 1] ^ s[k - 1];
        if(count.containsKey(s[k])){
    
    
            ans += (k - 1) * count.get(s[k]) - indexSum.get(s[k]);
        }
        count.put(s[k],count.getOrDefault(s[k],0) + 1);
        indexSum.put(s[k],indexSum.getOrDefault(s[k],0) + k);
    }
    return ans;
}

Although it is O(n) complexity for the test data, considering the search and setting of the hash table, the actual time efficiency is still higher than the first method above.

525. Continuous array

The solution to the reference question is organized as follows:
Define the difference in the number of 01s as the difference obtained by subtracting the number of 1s from the number of 0s in the subarray. minus[i] represents the difference in the number of 01s in the subarray [0,i]

Then if we can find subscript j that satisfies 0 <= j < i and minus[j] == minus[i], it does not mean that the difference in the number of 01s in the subarray [j + 1,i] is 0, that is, the number of 0s Is it a number equal to 1?

This requires us to calculate minus[i] when we traverse to i, and at the same time use the hash table to record the j whose number of 01s in the traversed part is equal to minus[i], and then we can calculate [j + 1,i] length. The maximum length of all subarrays that satisfy the condition is the final answer

How to calculate minus[i]? We don't need to explicitly calculate minus[i], we use a prefix and array prefix, prefix[i] represents the number of 1's in [0,i], then the total number of elements in [0,i] is i + 1 , the number of 1 is prefix[i], the number of 0 is i + 1 - prefix[i], then the difference in the number of 01 is i + 1 - 2 * prefix[i]

How to record in hash table? The key value represents minus[j], and the value value represents j. When traversing to i, the minus[i] of i is calculated as i + 1 - 2 * prefix[i], and you can find whether the hash table has a key. is i + 1 - 2 * prefix[i], if any, take out the value j, and calculate the length of the subarray as i - j (not i - j + 1, the qualified subarray here is [j + 1 ,i]); if not, put the key-value pair k = i + 1 - 2 * prefix[i], v = i

Just maintain the maximum length during the traversal process

In addition, since the range of the difference in the number of 01 can easily be known as [-n,n], we can use an array of size 2n + 1 as a hash table instead of using Map, saving the time required for the Map structure

public int findMaxLength(int[] nums) {
    
    
    int n = nums.length, max = 0;
    //计算前缀和数组
    int[] prefix = new int[n];
    prefix[0] = nums[0] ;
    for (int i = 1; i < n; i++) 
        prefix[i] = prefix[i - 1] + nums[i];
    int[] ht = new int[2 * n + 1]; //哈希表
    //哈希表初始化,由于后续计算中ht中元素值可能为0,所以需要初始化为-1表示未被赋值过
    Arrays.fill(ht,-1); 
    int minus,index;
    for (int i = 0; i < n; i++) {
    
    
        minus = i + 1 - 2 * prefix[i]; //计算i对应的 01数量差
        index = minus + n;             //计算 01数量差 对应的哈希表中的槽
        //如果ht[index]不为-1说明该槽被覆盖过,可以计算[index + 1,i]的长度
        if(ht[index] != -1) max = Math.max(max,i - ht[index]);
        //如果01数量差为0说明[0,i]就符合条件,长度为i + 1
        else if(minus == 0) max = Math.max(max,i + 1);
        //如果ht[index]为-1才更新ht[index]的值,因为我们需要的是符合条件的子数组的最大长度
        //对于不同的j拥有同一个01数量差,我们记录的应该是最小的j,这样计算得到的子数组长度i - j才会是最大的
        else ht[index] = i;
    }
    return max;
}

difference

For the integer one-dimensional array nums, construct a difference array dif of equal length, where dif[i] represents the difference obtained by subtracting nums[i - 1] from nums[i] (i > 0), diff[0] = nums[0]
Differential example diagram
can be inferred to obtain the original array based on the difference array. The meaning of the difference array is the difference between adjacent elements of the array.
When we want to do the same operation on all the array elements in a certain interval of the one-dimensional array, such as adding a number k (k can be a negative number), after the operation The difference between all numbers in the interval is the same as before the operation. That is to say, in the difference array, the value of this interval has not changed. Only the first number in the interval is different from its previous number. The difference is increased by k, and the difference between the last number in the interval (if any) and it is reduced by k, such as for [4,2,2,-5], for the interval [1,2 ] perform the operation of adding 3, that is, we get [4,5,5,-5], and the difference array is [4,1,0,-10], that is, after the operation, dif[1] = dif[1] + 3, dif [3] = dif[3] - 3
Considering that the last element in the original array does not have a subsequent number, if the interval operation includes the last element, you only need to add k to the first number in the interval corresponding to the value in the difference array. There is no need to add k to the interval. right border
If there is no difference array, the complexity of performing interval operations on the original array is O(n). If there are m interval operations, the total complexity of getting the final array is O(n * m); with the help of the difference array, The complexity of each interval operation is O(1). You only need to change the values ​​of the elements in the two difference arrays. To get the final array after m times of interval operations, you only need to traverse the difference arrays once and get the original array. Yes, the total complexity is O(1 * m + n) = O(m + n). If you
see that the question involves interval operations , you should first consider whether you can use difference arrays to solve the problem.

1094.Carpooling

The original array in this question has a length equal to the total number of stations. Each array element represents the maximum number of people on the bus when arriving at the station corresponding to the subscript.

Note that each trip[i] is an interval operation , that is, adding num_passengers to all the numbers in the interval of the original array [ start_location , end_location ). Note that end_location is not included , because end_location represents the num_passengers in this trip[i] The station where passengers want to get off means that these people should not be counted when arriving at this station, so what needs to be operated by + num_passengers is dif[ start_location ] in the difference array, and what needs to be operated by - num_passengers is dif[ end_location ] instead of dif[ end_location + 1]

public boolean carPooling(int[][] trips, int capacity) {
    
    
    int len = trips.length;
    //题目中说到0 <= trips[i][1] < trips[i][2] <= 1000,说明
    //车站的范围是[0,1000],所以数组最长为1001
    int[] dif = new int[1001];
    //记录区间操作过程中操作到的最右边的位置,在后面遍历差分数组时只需遍历到这个位置即可不用遍历到1000
    int maxR = -1;
    for(int i = 0;i < len;i++){
    
    
        dif[trips[i][1]] += trips[i][0];
        dif[trips[i][2]] -= trips[i][0];
        maxR = Math.max(maxR,trips[i][2]);
    }
    //逆推得到原数组
    for(int i = 1;i <= maxR;i++){
    
    
        dif[i] += dif[i - 1];
    }
    //判断原数组中是否有超过capacity的元素,即一个站出现的人数会不会超出capacity
    for(int i = 0;i <= maxR;i++){
    
    
        if(dif[i] > capacity){
    
    
            return false;
        }
    }
    return true;
}

1109.Flight booking statistics

The original array of this question is the array num with length n, where num[i] represents how many seats are reserved for flight number i + 1. Each bookings[i] is an interval operation, which means adding seats to all array elements in the specified interval in the num array.

public int[] corpFlightBookings(int[][] bookings, int n) {
    
    
    int[] res = new int[n];
    for(int i = 0;i < bookings.length;i++){
    
    
    	//数组下标从0开始,但是航班编号从1开始,所以区间操作的对象其实是
    	//[bookings[i][0] - 1,bookings[i][1] - 1]
        res[bookings[i][0] - 1] += bookings[i][2];
        //区间操作涉及到右边界的话,不对差分数组处理
        if(bookings[i][1] == n) continue;
        res[bookings[i][1]] -= bookings[i][2];
    }
    for(int i = 1;i < n;i++){
    
    
        res[i] += res[i - 1];
    }
    return res;
}

Guess you like

Origin blog.csdn.net/Pacifica_/article/details/123034552