给你一个二维矩阵 matrix
和一个整数 k
,矩阵大小为 m x n
由非负整数组成。
矩阵中坐标 (a, b)
的 目标值 可以通过对所有元素 matrix[i][j]
执行异或运算得到,其中 i
和 j
满足 0 <= i <= a < m
且 0 <= j <= b < n
(下标从 0 开始计数)。
请你找出 matrix
的所有坐标中第 k
大的目标值(k
的值从 1 开始计数)。
示例 1:
输入:matrix = [[5,2],[1,6]], k = 1 输出:7 解释:坐标 (0,1) 的目标值是 5 XOR 2 = 7 ,为最大的目标值。
示例 2:
输入:matrix = [[5,2],[1,6]], k = 2 输出:5 解释:坐标 (0,0) 的目标值是 5 = 5 ,为第 2 大的目标值。
示例 3:
输入:matrix = [[5,2],[1,6]], k = 3 输出:4 解释:坐标 (1,0) 的目标值是 5 XOR 1 = 4 ,为第 3 大的目标值。
示例 4:
输入:matrix = [[5,2],[1,6]], k = 4 输出:0 解释:坐标 (1,1) 的目标值是 5 XOR 2 XOR 1 XOR 6 = 0 ,为第 4 大的目标值。
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 1000
0 <= matrix[i][j] <= 10^6
1 <= k <= m * n
提示 1
Use a 2D prefix sum to precalculate the xor-sum of the upper left submatrix.
解法1:二维异或前缀 + 排序
思路与算法
我们用 ⊕ 表示按位异或运算。 由于「按位异或运算」与「加法运算」有着十分相似的性质,它们都满足
交换律: a⊕b=b⊕a
结合律: (a⊕b)⊕c=a⊕(b⊕c)
因此我们可以使用「前缀和」这一技巧对按位异或运算的结果进行维护。
由于本题中给定的矩阵 matrix 是二维的,因此我们需要使用二维前缀和。
设二维前缀和 pre(i, j) 表示矩阵 matrix 中所有满足 0≤x<i 且 0≤y<j 的元素执行按位异或运算的结果。与一维前缀和类似,要想快速得到 pre(i,j),我们需要已经知道 pre(i−1,j),pre(i,j−1) 以及 pre(i−1,j−1) 的结果,即:
pre(i,j)=pre(i−1,j)⊕pre(i,j−1)⊕pre(i−1,j−1)⊕matrix(i,j)
下图给出了该二维前缀和递推式的可视化展示。
当我们将 pre(i−1,j) 和 pre(i,j−1) 进行按位异或运算后,由于对一个数 x 异或两次 y,结果仍然为 x 本身,即:
x⊕y⊕y=x
因此 pre(i−1,j−1) 对应区域的按位异或结果被抵消,我们需要将其补上,并对位置 (i,j) 的元素进行按位异或运算,这样就得到了 pre(i,j)。
在得到了所有的二维前缀和之后,我们只需要找出其中第 k 大的元素即为答案。这一步我们可以直接将 mn 个二维前缀和进行排序后返第 k 大的元素。
Java版:
class Solution {
public int kthLargestValue(int[][] matrix, int k) {
int m = matrix.length;
int n = matrix[0].length;
int[][] prexor = new int[m + 1][n + 1];
List<Integer> res = new ArrayList<Integer>();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
prexor[i + 1][j + 1] = prexor[i + 1][j] ^ prexor[i][j + 1] ^ prexor[i][j] ^ matrix[i][j];
res.add(prexor[i + 1][j + 1]);
}
}
// 排序方式1
Collections.sort(res, new Comparator<Integer>() {
public int compare(Integer num1, Integer num2) {
return num2 - num1;
}
});
// 排序方式2
// res.sort(new Comparator<Integer>() {
// public int compare(Integer num1, Integer num2) {
// return num2 - num1;
// }
// });
return res.get(k - 1);
}
}
Python3版:
class Solution:
def kthLargestValue(self, matrix: List[List[int]], k: int) -> int:
m = len(matrix)
n = len(matrix[0])
prexor = [[0] * (n + 1) for _ in range(m + 1)]
res = []
for i in range(m):
for j in range(n):
prexor[i + 1][j + 1] = prexor[i + 1][j] ^ prexor[i][j + 1] ^ prexor[i][j] ^ matrix[i][j]
res.append(prexor[i + 1][j + 1])
res.sort(reverse=True)
return res[k - 1]
复杂度分析
- 时间复杂度:O(mnlog(mn))其中 m 和 n 分别为 matrix 的行数和列数。计算二维前缀和的时间复杂度为 O(mn),排序的时间复杂度为 O(mnlog(mn)),因此总时间复杂度为 O(mnlog(mn))。
- 空间复杂度:O(mn),即为存储二维前缀和需要的空间。