题目:
给你一个正整数 n ,表示最初有一个 n x n 、下标从 0 开始的整数矩阵 mat ,矩阵中填满了 0 。
另给你一个二维整数数组 query 。针对每个查询 query[i] = [row1i, col1i, row2i, col2i] ,请你执行下述操作:
- 找出 左上角 为
(row1i, col1i)且 右下角 为(row2i, col2i)的子矩阵,将子矩阵中的 每个元素 加1。也就是给所有满足row1i <= x <= row2i和col1i <= y <= col2i的mat[x][y]加1。
返回执行完所有操作后得到的矩阵 mat 。
算法:
方法一:模拟
func rangeAddQueries(n int, queries [][]int) [][]int {
ans := make([][]int, n)
for i := range ans {
ans[i] = make([]int, n)
}
for i := range queries {
for j := queries[i][0]; j <= queries[i][2]; j ++ {
for k := queries[i][1]; k <= queries[i][3]; k ++ {
ans[j][k] = ans[j][k] + 1
}
}
}
return ans
}
方法二:差分数组
方法一的暴力求解,如果querie次数很多,或者范围特别大,则可能时间复杂度达到O(n^3)。求对一维,二维区间多次运算后的结果,可以使用差分思想:
-
一维差分
差分数组diff的每个元素是diff[i] = nums[i] - nums[i - 1],如果对nums[i,j]进行区间+3,则只需要设置diff[i] = diff[i] + 3, diff[j + 1] = diff[j + 1] - 3。进行n这这样的运算,每次运行我们都可以在O(1)时间内完成。 然后我们利用 diff[i] = nums[i] - nums[i - 1]的特性,遍历遍历diff,num[i] = diff[i] + diff[i - 1],得到原始数组nums。 -
二维差分
一维前缀和prefix[i]是所有小于等于i的元素之和,类似的,二维前缀和prefixSum[i,j]的定义是:nums中所有row <= i, col <= j的元素之和(即nums[i,j]及其左上角的所有元素之和)。它可以用来求nums某个矩形范围内的和。
如果nums图中的位置增加1,则黄色区域所有元素的前缀和都增加1
如果只对蓝色的区域增加1,差分数据会进行如下变化:
每次query操作我们对二维差分数组进行上述操作即可,如何根据差分数组的到前缀和数组呢?从row,col两个方向求和就行
func rangeAddQueries(n int, queries [][]int) [][]int {
diff := make([][]int, n + 1)
for i := range diff {
diff[i] = make([]int, n + 1)
}
ans := make([][]int, n )
// update二维差分数组
for i := range queries {
diff[queries[i][0]][queries[i][1]] ++
diff[queries[i][0]][queries[i][3] + 1] --
diff[queries[i][2] + 1][queries[i][1]] --
diff[queries[i][2] + 1][queries[i][3] + 1] ++
}
// diff数组多出来的位置在d[n][n],第0行和第0列是有保存数据的
// 有差分数组构造ans数组
for i := 0; i < n; i ++ {
for j := 1; j < n; j ++ {
diff[i][j] = diff[i][j - 1] + diff[i][j]
}
}
for i := 1; i < n; i ++ {
for j := 0; j < n; j ++ {
diff[i][j] = diff[i - 1][j] + diff[i][j]
}
}
for i := 0; i < n; i ++ {
ans[i ] = make([]int, n)
for j := 0; j < n; j ++ {
ans[i][j] = diff[i][j]
}
}
return ans
}
另一种写法:
func rangeAddQueries(n int, queries [][]int) [][]int {
diff := make([][]int, n + 1)
ans := make([][]int, n + 1)
for i := range diff {
diff[i] = make([]int, n + 1)
ans[i] = make([]int, n + 1)
}
for i := range queries {
r1, c1, r2, c2 := queries[i][0],queries[i][1],queries[i][2],queries[i][3]
diff[r1][c1] ++
diff[r2 + 1][c1] --
diff[r1][c2 + 1] --
diff[r2 + 1][c2 + 1] ++
}
// 还原
for i := 1; i <= n; i ++ {
for j := 1; j <= n; j ++ {
ans[i][j] = ans[i - 1][j] + ans[i][j - 1] - ans[i - 1][j - 1] + diff[i - 1][j - 1]
}
}
ans = ans[1:]
for i := range ans {
ans[i] = ans[i][1:]
}
return ans
}
关键在于理解上面的ans数组是prefixSum数组