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
An alternate could be to apply the Binary Search on the “number range” instead of the “index range”
//对number range进行二分
class Solution {
public int kthSmallest(int[][] matrix, int k) {
int n = matrix.length;
int lo = matrix[0][0], hi = matrix[n-1][n-1];
while(lo < hi){
int mid = lo + ((hi-lo)>>1);
int[] smallbig = {Integer.MIN_VALUE, Integer.MAX_VALUE};
int count = handle(matrix, mid, smallbig);
if(count==k)
return smallbig[0];
else if(count<k)
lo = smallbig[1];
else
hi = smallbig[0];
}
//能执行到这里,比如案例[[-5]], 所以直接返回lo或者hi就可以了
return hi;
}
private int handle(int[][] matrix, int mid, int[] smallbig){
int count = 0, n = matrix.length;
//从矩阵的左下角开始移动, 根据矩阵的单调性, 要么往上移动, 要么往右移动
int i=n-1, j=0;
//统计矩阵中小于等于mid的元素数量
while(i>=0 && j<n){
if(matrix[i][j] <= mid){
//竖直方向上, (0,j)到(i,j)都小于等于mid
count += i+1;
smallbig[0] = Math.max(smallbig[0], matrix[i][j]);
j++;
}
else{
//水平方向上, (i,j)到(i,n-1)都大于mid
smallbig[1] = Math.min(smallbig[1], matrix[i][j]);
i--;
}
}
return count;
}
}
class Solution {
//内部类; 定义节点
class Node{
int x, y;
Node(int x, int y){
this.x=x;
this.y=y;
}
}
public int kthSmallest(int[][] matrix, int k) {
int n = matrix.length;
//java新特性; 使用lambda表达式写比较器; 按照值的大小升序排序
Queue<Node> minHeap = new PriorityQueue<Node>((a,b)->matrix[a.x][a.y] - matrix[b.x][b.y]);
//先把矩阵每一行的第一个元素添加至最小堆
for(int i=0; i<n; i++){
minHeap.add(new Node(i,0));
}
//找到第k个元素
int count = 0;
while(true){
Node cur = minHeap.poll();
int val = matrix[cur.x][cur.y];
count++;
if(count==k)
return val;
//本题最核心: cur不是第k个节点的话, 就把cur所在行的下一个节点加入最小堆
//注意检查索引是否越界
if(cur.y+1<n){
cur.y++;
minHeap.add(cur);
}
}
}
}
import java.util.*;
class Node {
int row;
int col;
Node(int row, int col) {
this.row = row;
this.col = col;
}
}
class Solution {
public static int findKthSmallest(int[][] matrix, int k) {
PriorityQueue<Node> minHeap = new PriorityQueue<Node>((n1, n2) -> matrix[n1.row][n1.col] - matrix[n2.row][n2.col]);
// put the 1st element of each row in the min heap
// we don't need to push more than 'k' elements in the heap
for (int i = 0; i < matrix.length && i < k; i++)
minHeap.add(new Node(i, 0));
// take the smallest (top) element form the min heap, if the running count is equal to k return the number
// if the row of the top element has more elements, add the next element to the heap
int numberCount = 0, result = 0;
while (!minHeap.isEmpty()) {
Node node = minHeap.poll();
result = matrix[node.row][node.col];
if (++numberCount == k)
break;
node.col++;
if (matrix[0].length > node.col)
minHeap.add(node);
}
return result;
}
}
讲解的太棒了, 复习时看看原链接
An alternate could be to apply the Binary Search on the “number range” instead of the “index range”. As we know that the smallest number of our matrix is at the top left corner and the biggest number is at the bottom lower corner. These two number can represent the “range” i.e., the start and the end for the Binary Search. Here is how our algorithm will work:
Start the Binary Search with start = matrix[0][0] and end = matrix[n-1][n-1].
Find middle of the start and the end. This middle number is NOT necessarily an element in the matrix.
Count all the numbers smaller than or equal to middle in the matrix. As the matrix is sorted, we can do this in O(N).
While counting, we can keep track of the “smallest number greater than the middle” (let’s call it n1) and at the same time the “biggest number less than or equal to the middle” (let’s call it n2). These two numbers will be used to adjust the "number range" for the Binary Search in the next iteration.
If the count is equal to ‘K’, n1 will be our required number as it is the “biggest number less than or equal to the middle”, and is definitely present in the matrix.
If the count is less than ‘K’, we can update start = n2 to search in the higher part of the matrix and if the count is greater than ‘K’, we can update end = n1 to search in the lower part of the matrix in the next iteration.
class Solution {
public static int findKthSmallest(int[][] matrix, int k) {
int n = matrix.length;
int start = matrix[0][0], end = matrix[n - 1][n - 1];
while (start < end) {
int mid = start + (end - start) / 2;
// first number is the smallest and the second number is the largest
int[] smallLargePair = { matrix[0][0], matrix[n - 1][n - 1] };
int count = countLessEqual(matrix, mid, smallLargePair);
if (count == k)
return smallLargePair[0];
if (count < k)
start = smallLargePair[1]; // search higher
else
end = smallLargePair[0]; // search lower
}
return start;
}
private static int countLessEqual(int[][] matrix, int mid, int[] smallLargePair) {
int count = 0;
//核心: 以矩阵的左下角为起点, 每次要么往上移动, 要么往右移动
int n = matrix.length, row = n - 1, col = 0;
while (row >= 0 && col < n) {
if (matrix[row][col] > mid) {
// as matrix[row][col] is bigger than the mid, let's keep track of the
// smallest number greater than the mid
smallLargePair[1] = Math.min(smallLargePair[1], matrix[row][col]);
row--;
} else {
// as matrix[row][col] is less than or equal to the mid, let's keep track of the
// biggest number less than or equal to the mid
smallLargePair[0] = Math.max(smallLargePair[0], matrix[row][col]);
count += row + 1;
col++;
}
}
return count;
}
}