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.
解法一:
这题我们也可以用二分查找法来做,我们由于是有序矩阵,那么左上角的数字一定是最小的,而右下角的数字一定是最大的,所以这个是我们搜索的范围,然后我们算出中间数字mid,由于矩阵中不同行之间的元素并不是严格有序的,所以我们要在每一行都查找一下mid,我们使用upper_bound,这个函数是查找第一个大于目标数的元素,如果目标数在比该行的尾元素大,则upper_bound返回该行元素的个数,如果目标数比该行首元素小,则upper_bound返回0, 我们遍历完所有的行可以找出中间数是第几小的数,然后k比较,进行二分查找,本解法的整体时间复杂度为O(nlgn*lgX),其中X为最大值和最小值的差值,参见代码如下:
上面的解法还可以进一步优化到O(nlgX),其中X为最大值和最小值的差值,我们并不用对每一行都做二分搜索法,我们注意到每列也是有序的,我们可以利用这个性质,从数组的左下角开始查找,如果比目标值小,我们就向右移一位,而且我们知道当前列的当前位置的上面所有的数字都小于目标值,那么cnt += i+1,反之则向上移一位,这样我们也能算出cnt的值。其余部分跟上面的方法相同,参见代码如下:
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.
文字解释转自http://www.cnblogs.com/grandyang/p/5727892.html
解法一:
c++:
class Solution {
public:
int kthSmallest(vector<vector<int>>& matrix, int k) {
priority_queue<int> q;
for(int i = 0; i < matrix.size(); ++i){
for(int j = 0; j< matrix[0].size(); ++j){
q.emplace(matrix[i][j]);
if(q.size()>k) q.pop();
}
}
return q.top();
}
};
c语言(自己写的优先级队列),结果不对:
//自己用c语言实现priority_queue
typedef struct priority_queue{
int A[10000000];
int size; //queue的大小,不是数组的大小
}priority_queue;
int PARENT(i) { return (i-1)/2; }
int LEFT(i) { return 2*i + 1; }
int RIGHT(i) { return 2*i + 2; }
//辅助函数
void MAX_HEAPIFY(priority_queue* q, int i){ //从i节点开始,逐层往下调整
int l = LEFT(i);
int r = RIGHT(i);
int largest;
if(l < q->size && q->A[i] > q->A[l])
largest = i;
else largest = l;
if(r < q->size && q->A[largest] < q->A[r])
largest = r;
if(largest != i){
int tmp = q->A[largest];
q->A[largest] = q->A[i];
q->A[i] = tmp;
}
}
//辅助函数
void INCREASE_KEY(priority_queue* q, int i, int key){ //从i开始,逐层往上调整
if(key > q->A[i]){
q->A[i] = key;
int tmp, p;
while(i > 0){
p = PARENT(i);
if(q->A[i] > q->A[p]){
tmp = q->A[p];
q->A[p] = q->A[i];
q->A[i] = tmp;
}
i = p;
}
}
}
//插入元素
void insert(priority_queue* q, int key){
q->A[q->size] = INT_MIN;
q->size++;
INCREASE_KEY(q, q->size-1, key);
}
int kthSmallest(int** matrix, int matrixRowSize, int matrixColSize, int k){
priority_queue* q = (priority_queue*)malloc(sizeof(priority_queue));
q->size = 0;
for(int i = 0; i < matrixRowSize; ++i){
for(int j = 0; j < matrixColSize; ++j){
insert(q, matrix[i][j]);
if(q->size > k){ //只能维持k大小的q
q->A[0] = q->A[q->size-1];
q->size--;
MAX_HEAPIFY(q, 0);
}
}
}
return q->A[0];
}
这题我们也可以用二分查找法来做,我们由于是有序矩阵,那么左上角的数字一定是最小的,而右下角的数字一定是最大的,所以这个是我们搜索的范围,然后我们算出中间数字mid,由于矩阵中不同行之间的元素并不是严格有序的,所以我们要在每一行都查找一下mid,我们使用upper_bound,这个函数是查找第一个大于目标数的元素,如果目标数在比该行的尾元素大,则upper_bound返回该行元素的个数,如果目标数比该行首元素小,则upper_bound返回0, 我们遍历完所有的行可以找出中间数是第几小的数,然后k比较,进行二分查找,本解法的整体时间复杂度为O(nlgn*lgX),其中X为最大值和最小值的差值,参见代码如下:
解法二:
c语言:
int upper_bound(int* arr, int size, int target){ //O(lgN)
int l = 0, r = size - 1;
int mid = 0;
while(l <= r){
mid = (l + r)/2;
if(target >= arr[mid])
l = mid + 1;
else if(target < arr[mid])
r = mid - 1;
}
return l;
}
int kthSmallest(int** matrix, int matrixRowSize, int matrixColSize, int k) {
int min = matrix[0][0];
int max = matrix[matrixRowSize-1][matrixRowSize-1];
int mid = 0, cnt;
while(min < max){ //O(lgX)
cnt = 0;
mid = (max + min)/2;
for(int i = 0; i < matrixRowSize; ++i) //O(N)
cnt += upper_bound(matrix[i], matrixColSize, mid);
if(cnt < k) min = mid + 1;
else max = mid;
}
return min;
}
上面的解法还可以进一步优化到O(nlgX),其中X为最大值和最小值的差值,我们并不用对每一行都做二分搜索法,我们注意到每列也是有序的,我们可以利用这个性质,从数组的左下角开始查找,如果比目标值小,我们就向右移一位,而且我们知道当前列的当前位置的上面所有的数字都小于目标值,那么cnt += i+1,反之则向上移一位,这样我们也能算出cnt的值。其余部分跟上面的方法相同,参见代码如下:
解法三:
c语言:
int leftBottom2topRight (int **matrix, int matrixRowSize, int matrixColSize, int target){ //O(N)
int i = matrixRowSize - 1, j = 0; int cnt = 0;
while(i >= 0 && j < matrixColSize){
if(matrix[i][j] <= target){
cnt += i + 1;
++j;
}
else --i;
}
return cnt;
}
int kthSmallest(int** matrix, int matrixRowSize, int matrixColSize, int k) {
int min = matrix[0][0];
int max = matrix[matrixRowSize-1][matrixRowSize-1];
int mid = 0, cnt;
while(min < max){
mid = (max + min)/2;
cnt = leftBottom2topRight(matrix, matrixRowSize, matrixColSize, mid);
if(cnt < k) min = mid + 1;
else max = mid;
}
return min;
}