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学习算法和刷题的思路指南