1. 解析
题目大意,在行和列有序的二维矩阵中查找第k个最小的数。
2. 分析
刚开始看到题目,我以为利用Search a 2D Matrix II的思路解决就可以,折腾半天发现,好像有点不太一样。其实本质还是一样的,只不过我没想出来。比较容易想到的解法就是利用优先队列priority_queue,默认是大根堆,遍历矩阵,将矩阵中的每一个元素放进堆当中,若堆中元素的个数大于k,去掉堆顶元素,这样可以保证堆中存储的始终是矩阵中前k个最小的数,最后将返回堆顶即可。
class Solution {
public:
int kthSmallest(vector<vector<int>>& matrix, int k){
priority_queue<int> res;
for (auto nums : matrix){
for (int num : nums){
res.push(num);
if (res.size() > k) res.pop(); //若当前堆中元素大于k个,删除堆顶元素
}
}
return res.top();
}
};
3. 折半查找解法
参考@Grandyang的思路,为了和Search a 2D Matrix II联系起来,我主要就讲解search2的解法。因为是行和列有序,所以若以左下角的元素为起点,那么往右移动元素递增,往上移动元素递减,根据这个可以统计出矩阵中小于target目标的个数。且左上角的元素left小于右下角的元素right,这样可以采用二分查找的方式,每次以mid = (left + right) / 2作为目标值,然后统计当前小于目标值的个数cnt,若cnt < k,说明当前元素mid无法获取k个元素,left = mid + 1; 反之, 在(left, mid) 范围内能够获取大于k个元素,缩小范围成right = mid,在(left, mid) 范围查找。
class Solution {
public:
int kthSmallest(vector<vector<int>>& matrix, int k){
int left = matrix[0][0], right = matrix.back().back();
while (left < right){
int mid = (left + right) / 2;
int cnt = search1(matrix, mid);
//int cnt = search2(matrix, mid);
if (cnt < k) left = mid + 1;
else right = mid;
}
return left;
}
int search1(vector<vector<int>>& matrix, int target){
int res = 0;
for (auto nums : matrix){
res += upper_bound(nums.begin(), nums.end(), target) - nums.begin();
}
return res;
}
int search2(vector<vector<int>>& matrix, int target){
int res = 0;
int i = matrix.size() - 1, j = 0; //左下角坐标
while (i >= 0 && j < matrix[i].size()){
if (matrix[i][j] <= target){
res += i + 1;
j++;
}
else{
i--;
}
}
return res;
}
};