Kth Smallest Element in a Sorted Matrix
Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth smallest element in the matrix.
Note that it is the kth smallest element in the sorted order, not the kth distinct element.
Example:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,
return 13. Note: You may assume k is always valid, 1 ≤ k ≤ n2.
Tips:
思路1(Heap):
- Build a minHeap of elements from the first row.
Do the following operations k-1 times : Every time when you poll out the root(Top Element in Heap), you need to know the row number and column number of that element(so we can create a Node class here), replace that root with the next element from the same column.
这种思路下先将第一行加入heap,然后取heap里的最小值,加入矩阵中这个最小值下面的数,要注意的是要新建一个node类,并重写compareTo方法,不然无法获取这个点的坐标。注意在重写compareTo那里一定要public,不然会出错!
思路2(binary search):
这题我们也可以用二分查找法来做,我们由于是有序矩阵,那么左上角的数字一定是最小的,而右下角的数字一定是最大的,所以这个是我们搜索的范围,然后我们算出中间数字mid,由于矩阵中不同行之间的元素并不是严格有序的,所以我们要在每一行都查找一下mid,我们使用upper_bound,这个函数是查找第一个大于目标数的元素,如果目标数在比该行的尾元素大,则upper_bound返回该行元素的个数,如果目标数比该行首元素小,则upper_bound返回0, 我们遍历完所有的行可以找出中间数是第几小的数,然后k比较,进行二分查找,本解法的整体时间复杂度为O(nlgn*lgX),其中X为最大值和最小值的差值。
思路3(Heap):
和思路一类似,但是不需要提前加入第一行,而是改为在找到当前最小值后,加入最小值的右边和下边两个元素。需要注意的是这个时候需要判断要加入heap的元素是否已经在别的地方加入到heap里了。所以需要新建n*n的matrix hash[][]来判断。
Code:
解法1:
public class Solution {
public int kthSmallest(int[][] matrix, int k) {
int n = matrix.length;
if (k == n * n) {
return matrix[n - 1][n - 1];
}
PriorityQueue<Node> heap = new PriorityQueue<>();
for (int i = 0; i < n; i++) {
heap.add(new Node(0, i, matrix[0][i]));
}
for (int i = 0; i < k - 1; i++) {
Node curt = heap.remove();
if (curt.x == n - 1) {
continue;
}
heap.add(new Node(curt.x + 1, curt.y, matrix[curt.x + 1][curt.y]));
}
return heap.remove().val;
}
}
class Node implements Comparable<Node> {
int x;
int y;
int val;
public Node(int x, int y, int val) {
this.x = x;
this.y = y;
this.val = val;
}
public int compareTo (Node that) {
return this.val - that.val;
}
}
解法2:
public int kthSmallest(int[][] matrix, int k) {
int m=matrix.length;
int lower = matrix[0][0];
int upper = matrix[m-1][m-1];
while(lower<upper){
int mid = lower + ((upper-lower)>>1);
int count = count(matrix, mid);
if(count<k){
lower=mid+1;
}else{
upper=mid;
}
}
return upper;
}
private int count(int[][] matrix, int target){
int m=matrix.length;
int i=m-1;
int j=0;
int count = 0;
while(i>=0&&j<m){
if(matrix[i][j]<=target){
count += i+1;
j++;
}else{
i--;
}
}
return count;
}
解法3:
class Number {
public int x, y, val;
public Number(int x, int y, int val) {
this.x = x;
this.y = y;
this.val = val;
}
}
class NumComparator implements Comparator<Number> {
public int compare(Number a, Number b) {
return a.val - b.val;
}
}
public class Solution {
/**
* @param matrix: a matrix of integers
* @param k: an integer
* @return: the kth smallest number in the matrix
*/
private boolean valid(int x, int y, int[][] matrix, boolean[][] hash) {
if(x < matrix.length && y < matrix[x].length && !hash[x][y]) {
return true;
}
return false;
}
int dx[] = {0,1};
int dy[] = {1,0};
public int kthSmallest(int[][] matrix, int k) {
// write your code here
if (matrix == null || matrix.length == 0) {
return -1;
}
if (matrix.length * matrix[0].length < k) {
return -1;
}
PriorityQueue<Number> heap = new PriorityQueue<Number>(k, new NumComparator());
boolean[][] hash = new boolean[matrix.length][matrix[0].length];
heap.add(new Number(0, 0, matrix[0][0]));
hash[0][0] = true;
for (int i = 0; i < k - 1; i ++) {
Number smallest = heap.poll();
for (int j = 0; j < 2; j++) {
int nx = smallest.x + dx[j];
int ny = smallest.y + dy[j];
if (valid(nx, ny, matrix, hash)) {
hash[nx][ny] = true;
heap.add(new Number(nx, ny, matrix[nx][ny]));
}
}
}
return heap.peek().val;
}
}