leetcode之前缀和刷题总结2

Posted nuist__NJUPT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode之前缀和刷题总结2相关的知识,希望对你有一定的参考价值。

leetcode之前缀和刷题总结2

1-区域和检索-数组不可变
题目链接:题目链接戳这里!!!

思路:简单的前缀和,在初始化的时候使用sums数组存储nums数组的前缀和,后面求nums数组中left到right的区间和,可以直接sums[right]-sums[left-1],线性时间求得。

class NumArray 
    int [] sums ;
    public NumArray(int[] nums) 
        sums = new int [nums.length] ;
        sums[0] = nums[0] ;
        for(int i=1; i<sums.length; i++)
            sums[i] = sums[i-1] + nums[i] ;
        
    
    
    public int sumRange(int left, int right) 
        if(left>=1)
        return sums[right]-sums[left-1]  ;
        else  
            return sums[right] ;
        
    


/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray obj = new NumArray(nums);
 * int param_1 = obj.sumRange(left,right);
 */


2-二维区间和检索-矩阵不可变
题目链接:题目链接戳这里!!!

思路:将二维前缀和改造成按每行的一维前缀和,然后对于每行求区间和,然后累加即可。

class NumMatrix 
    int [][] matrix ;
    public NumMatrix(int[][] matrix) 
        
        for(int i=0; i<matrix.length; i++)
            for(int j=1; j<matrix[0].length; j++)
                matrix[i][j] += matrix[i][j-1] ;//按行改造成一维前缀和 
            
        
        this.matrix = matrix ;
    
    
    public int sumRegion(int row1, int col1, int row2, int col2) 
        int sum = 0 ;
        for(int i=row1; i<=row2; i++) //对每一行,求两列之间的区间和
            if(col1-1>=0)
                sum += matrix[i][col2] - matrix[i][col1-1] ;
            else
                sum += matrix[i][col2] ;
            
        
        return sum ;
    


/**
 * Your NumMatrix object will be instantiated and called as such:
 * NumMatrix obj = new NumMatrix(matrix);
 * int param_1 = obj.sumRegion(row1,col1,row2,col2);
 */


3-矩阵区域和
题目链接:题目链接戳这里!!!

思路:这题用到二维区间和公式,仅从解题堆的角度说,记住公式,马上就可以求解出答案。

矩阵区间和公式如下:

对应得映射公式如下:

class Solution 
    public int[][] matrixBlockSum(int[][] mat, int k) 
        //这题需要用到二位前缀和,记住公式即可
        int m = mat.length ; 
        int n = mat[0].length ;
        int [][] f = new int [m+1][n+1] ;
        for(int i=0; i<m; i++)
            for(int j=0; j<n; j++)
                f[i+1][j+1] = f[i][j+1] + f[i+1][j] - f[i][j] + mat[i][j] ;
            
        
        int [][] res = new int [m][n] ;
        for(int i=0; i<m; i++)
            for(int j=0; j<n; j++)
                int row1 = Math.max(0,i-k) ;
                int col1 = Math.max(0,j-k) ;
                int row2 = Math.min(i+k,m-1) ;
                int col2 = Math.min(j+k,n-1) ;
                res[i][j] = f[row2+1][col2+1] - f[row1][col2+1] - f[row2+1][col1] + f[row1][col1] ;
            
        
        return res ;
    


4-拼车
题目链接:题目链接戳这里!!!

思路:使用site数组记录i时刻得上下车人数site[i],然后遍历site数组求出每个时刻车上的人数,如果大于capcity,则超载。

class Solution 
    public boolean carPooling(int[][] trips, int capacity) 
        int [] site = new int [1001] ;
        for(int []trip : trips)
            site[trip[2]] -= trip[0] ;
            site[trip[1]] += trip[0] ;
        
        int total = 0 ;
        for(int people : site)
            total += people ;
            if(total>capacity)
                return false ;
            
        
        return true ;
    


5-航班预定统计
题目链接:题目链接戳这里!!!

思路:差分数组+前缀和

注意到一个预订记录实际上代表了一个区间的增量。我们的任务是将这些增量叠加得到答案。因此,我们可以使用差分解决本题。

差分数组对应的概念是前缀和数组,对于数组 [1,2,2,4],其差分数组为 [1,1,0,2],差分数组的第 i 个数即为原数组的第 i个元素和第 i-1个元素的差值,也就是说我们对差分数组求前缀和即可得到原数组。

bookings二维数组每一行有三个数,前两个数代表航班区间左右端点,最后一个数代表数量,这个数量只对区间内的航班“有效”,所以在左端点进行+操作,在区间结束的第一个位置进行-操作然后对差分数组求前缀和即可。

举个例子,比如[2,4,10]这三个数,表明从航班编号2、3、4处都预定了10个位置,所以10这个数量只对2、3、4有效,在差分数组的2处+10,在差分数组的5处,10失去作用,减去10即可。

class Solution 
    public int[] corpFlightBookings(int[][] bookings, int n) 
        int [] ans = new int [n] ;
        for(int [] book : bookings)
            ans[book[0]-1] += book[2] ;
            if(book[1] < n)
                ans[book[1]] -= book[2] ;
            
        
        for(int i=1; i<n; i++)
            ans[i] += ans[i-1] ;
          
        return ans ;
    


6-子数组异或查询
题目链接:题目链接戳这里!!!

思路:使用prefix数组求出当前位置与之前所有元素的异或值,然后在每次询问中去区间的异或值即可。

class Solution 
    public int[] xorQueries(int[] arr, int[][] queries) 
        int [] prefix = new int [arr.length] ;
        prefix[0] = arr[0] ;
        for(int i=1; i<arr.length; i++)
            prefix[i] = prefix[i-1] ^ arr[i] ; 
        
        int [] res = new int [queries.length] ;
        for(int i=0; i<queries.length; i++)
            if(queries[i][0]-1>=0)
               res[i] = prefix[queries[i][1]] ^ prefix[queries[i][0]-1] ;
            else
                res[i] = prefix[queries[i][1]] ;
            
        
        return res ;
    


7-所有奇数长度子数组的和
题目链接:题目链接戳这里!!!

思路:求出数组元素的前缀和,然后累加奇数个数的区间和即可。

class Solution 
    public int sumOddLengthSubarrays(int[] arr) 
        int [] sums = new int [arr.length] ;
        sums[0] = arr[0] ;
        for(int i=1; i<arr.length; i++)
            sums[i] = sums[i-1] + arr[i] ;
        
        int res = 0 ;
        for(int i=-1; i<sums.length; i++)
            for(int j=i+1; j<sums.length; j+=2)
                if(i==-1)
                    res += sums[j] ;
                else
                    res += sums[j] - sums[i] ;
                
            
        
        return res ;
    


8-表现良好的最长时间段
题目链接:题目链接戳这里!!!

思路1:暴力法
由于这一题的测试用例不多,对时间也没要求,2.4s的执行时间也通过了。

class Solution 
    public int longestWPI(int[] hours) 
        int ans = 0 ;
       for(int i=0; i<hours.length; i++)
           int tireDay = 0, freeDay = 0 ;
           for(int j=i; j<hours.length; j++)
               if(hours[j]>8)
                   tireDay++ ;
               else
                   freeDay++ ;
               
               if(tireDay>freeDay)
                   ans = Math.max(ans, tireDay+freeDay) ;
               
           
       
       return ans ;
    


思路2:前缀和+单调栈
以输入样例 hours = [9, 9, 6, 0, 6, 6, 9] 为例,我们将大于 8 小时的一天记为 1 分,小于等于 8 小时的一天记为 −1 分。那么处理后,我们得到 hours = [1, 1, -1, -1, -1, -1, 1],然后我们对得分数组计算前缀和 presum = [0, 1, 2, 1, 0, -1, -2, -1](数组最前面设置一位 0:目的是作为后面单调栈的一个比较标准,选出负数且严格单调减)。

题目要求返回表现良好时间段的最大长度,即求最长的一段中,得分 1 的个数大于得分 −1 的个数,也就是求 hour 数组中最长的一段子数组,其和大于 0,那么也就是找出前缀和数组 presum 中两个索引 i 和 j,使 j - i 最大,且保证 presum[j] - presum[i] 大于 0。到此,我们就将这道题转化为:求 presum 数组中的一个最长的上坡,可以用单调栈实现。

维护一个单调栈,其中存储 presum 中的元素索引,栈中索引指向的元素严格单调递减,由 presum 数组求得单调栈为 stack = [0, 5, 6],其表示元素为 [0, -1, -2]。然后我们从后往前遍历 presum 数组,与栈顶索引指向元素比较,如果相减结果大于 0,则一直出栈,直到不大于 0 为止,然后更新当前最大宽度。

class Solution 
    public int longestWPI(int[] hours) 
        int  [] presum = new int [hours.length+1] ;
       for(int i=0; i<hours.length; i++)
           hours[i] = hours[i]>8 ? 1 : -1 ;
       

       for(int i=1;i<presum.length; i++)
           presum[i] = presum[i-1] + hours[i-1] ;
       
       Stack<Integer> stack = new Stack<>() ;
       int result = 0 ;
       stack.push(0) ;
       for(int i=1; i<presum.length; i++)
           if(stack.isEmpty() || presum[stack.peek()] > presum[i])
               stack.push(i) ;
           
       
       for(int i=presum.length-1; i>=0; i--)
              while(!stack.isEmpty() && presum[i] > presum[stack.peek()]) 
                result = Math.max(result, i - stack.pop());
            
           
       
       return result ;

    


9-有序数组中差绝对值之和
题目链接:题目链接戳这里!!!

思路:前缀和+找规律

class Solution 
    public int[] getSumAbsoluteDifferences(int[] nums) 
        int [] sums = new int [nums.length+1] ;
        for(int i=1; i<sums.length; i++)
            sums[i] = sums[i-1] + nums[i-1] ;
        
        int [] result = new int [nums.length]  ;
        for(int i=1; i<sums.length; i++)
            int left = i * nums[i-1] - sums[i] ;
            int right = sums[sums.length-1] - sums[i] - (sums.length-1-i) * nums[i-1] ;
            result[i-1] = left + right ;
        
        return result ;
    


10-一维数组的动态和
题目链接:题目链接戳这里!!!

library要闭馆了,今天闭馆太早了,水个easy题,结束!!!

class Solution 
    public int[] runningSum(int[] nums) 
        int [] res = new int [nums.length] ;
        res[0] = nums[0] ;
        for(int i=1; i学习算法和刷题的思路指南

leetcode之贪心算法刷题总结2

太棒了!字节跳动工程师在GitHub开源了一份刷题总结,狂揽8.2K星,霸屏GitHub!

leetcode之回溯刷题总结2

leetcode之模拟刷题总结2

leetcode之动态规划刷题总结2