Given an n x n matrix where each of the rows and columns is sorted in ascending order, return the kth smallest element in the matrix.
Note that it is the kth smallest element in the sorted order, not the kth distinct element.
You must find a solution with a memory complexity better than O(n2).
Example 1:
Input: matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8
Output: 13
Explanation: The elements in the matrix are [1,5,9,10,11,12,13,13,15], and the 8th smallest number is 13
Example 2:
Input: matrix = [[-5]], k = 1
Output: -5
矩阵的每行每列都是排好序的,返回第k小的元素。
注意每行的元素并不是全都比下一行的元素小。
思路:
Binary Search
如果保证每行都比下一行的元素小,那就比较好办,直接binary search就行。
现在的难点在于每行不一定都比下一行元素小,只是保证了每行和每列是排好序的,
比如例子1中第2行有13,而第3行有比它小的12。
所以这里的binary search要换个思路,找到比mid 值小的元素有多少个,
如果比k个少,说明这个mid值应该大一些,需要提高左边界,
同样的,如果比k个多,说明mid应该小一些,需要降低右边界。
直到左右边界重合,则找到了第k小的元素。
那么怎么找比mid值小的有多少个呢,
可以一行一行逐个元素地比较,记下个数。
也有一个比较巧的方法,就是从左下角的元素开始,一列一列地看,
举个例子,
1, 5, 9
10, 11, 13
12, 13, 15
假如mid = 12, 首先看第1列(到左下角的元素12),
12 <= mid, 而列又是排好序的,所以整个一列的3个元素都记入,count += 3
下一列,13>mid, 所以往上一行,11 < mid, 那么11上面的列也要记入, count += 2,
再往右,到13,13>mid, 往上一行,9 < mid, 记入count += 1,
再下一列,超出边界,终止。
这个是从左下角到右上角的遍历方法。
当然也可以逐行逐元素遍历。
public int kthSmallest(int[][] matrix, int k) {
int n = matrix.length;
int low = matrix[0][0];
int high = matrix[n-1][n-1];
while(low < high) {
int mid = low + (high - low) / 2;
int count = lessThan(matrix, mid);
if(count < k) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}
int lessThan(int[][] matrix, int target) {
int n = matrix.length;
int i = n - 1;
int j = 0;
int count = 0;
while(i >= 0 && j < n) {
if(matrix[i][j] > target) {
i --;
} else {
count += (i + 1);
j ++;
}
}
return count;
}