题目: 给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素。
请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素。
输入:
matrix = [ k = 8,
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
输出: 13。
首先第k大数一定落在[l, r]中,其中l = matrix[0][0], r = matrix[row - 1][col - 1].
我们二分值域[l, r]区间,mid = (l + r) >> 1, 对于mid,我们检查矩阵中有多少元素小于等于mid,记个数为cnt,那么有:
1、如果cnt < k, 那么[l, mid]中包含矩阵元素个数一定< k,
那么第k小元素一定不在[l, mid]中,必定在[mid + 1, r]中,所以更新l = mid + 1.
2、否则cnt >= k,那么[l, mid]中包含矩阵元素个数就>=k,
即第k小元素一定在[l,mid]区间中,令r = mid;
至于怎么得矩阵中有多少元素小于等于mid,
可以利用矩阵本身性质,从右上角开始枚举,具体可参考代码。
public int kthSmallest(int[][] matrix, int k) {
int rows = matrix.length, cols = matrix[0].length;
int start = matrix[0][0], end = matrix[rows - 1][cols - 1];
while (start < end) {
int mid = ((end - start) >> 1) + start;
int count = helper(matrix, mid);
// 不能在下面返回,mid可能不在矩阵中,让二分自然结束之后返回
// if (count == k) return mid;
if (count < k) start = mid + 1; // count<k 不是 < mid
else end = mid;
}
return start;// count=k时不返回,保证了退出循环时,start一定在矩阵里
}
// 统计矩阵中<=mid 的数的个数
int helper(int[][] matrix, int mid) {
int count = 0;
int i = 0, j = matrix[0].length - 1;
while (i < matrix.length && j >= 0) {
if (matrix[i][j] > mid) {
j--;
} else {
// 这里计算count不要误以为需要加上上面几行的i*matrix.len
// 因为i在上一行的时候已经加过了
count += (j + 1);
i++;
}
}
return count;
}
时间复杂度为O(n*log(m)), n = max(row, col) m代表二分区间的长度,即矩阵最大值和最小值的差