给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素。
请注意,它是排序后的第k小元素,而不是第k个元素。
示例:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,
返回 13。
思路:
- 二分查找, 并计算每一次小于等于中间值的元素个数
- 如果<k, 前半段舍掉, 否则后半段舍掉
- 计算小于等于m的个数时, 分别对每一行再次二分查找
-
class Solution { public int kthSmallest(int[][] matrix, int k) { int r = matrix.length; int c = matrix[0].length; int l = matrix[0][0]; int h = matrix[r-1][c-1]; while(l < h) { int m = (l+h)/2; int count = findNumber(matrix, m);//计算matrix里小于等于m的数. 为什么不能只是小于?因为底下一行已经没有把m考虑在内了,直接跳过m, l = m+1, 所以现在必须把m考虑进来 if(count < k) l = m+1; else h = m; } return l; } public int findNumber(int[][] matrix, int a) { int r = matrix.length; int c = matrix[0].length; int count = 0; for(int i = 0; i< r; i++) { int l = 0; int h = c; while(l < h) { int m = (l+h)/2; if(matrix[i][m]<= a)l = m+1;//为什么必须小于等于? else h = m; } count += l; } return count; } }
这里有个细节问题, 我纠结了一会后来才想通:
-
为什么必须要计算小于等于m的元素个数, 不能只是小于m的元素个数?
-
因为底下一行已经没有把m考虑在内了,直接跳过m, l = m+1, 所以现在必须把等于m的个数考虑进来.
-
所以相应的,也可以有另外一种写法, 底下一行再考虑等于m的情况.
-
class Solution { public int kthSmallest(int[][] matrix, int k) { int r = matrix.length; int c = matrix[0].length; int l = matrix[0][0]; int h = matrix[r-1][c-1]; while(l < h) { int m = (l+h+1)/2; int count = findNumber(matrix, m); if(count < k) l = m; else h = m+1; } return l; } public int findNumber(int[][] matrix, int a) { int r = matrix.length; int c = matrix[0].length; int count = 0; for(int i = 0; i< r; i++) { int l = 0; int h = c; while(l < h) { int m = (l+h)/2; if(matrix[i][m]< a)l = m+1; else h = m; } count += l; } return count; } }