问题简述
给定一个
n×n
的矩阵,矩阵中每行和每列的元素都按升序排列。给定一个
k(k∈[1,n2])
, 求再整个矩阵中按从小到大排序为
k
的元素。
例如:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,
return 13.
解题思路
该矩阵的行和列都是按从小到大的顺序排列的,这样的问题一般都是可以用二分法解决的。再来看,我们的输入
首先,我们以整个矩阵中的最小值为下边界(左上角元素),最大值为上边界(右下角元素)。然后用二分的方法猜过去,猜输出的结果是多少。只要满足猜的条件,我们就会把整个元素记录下来,然后看看有没有比这个元素小,并且满足猜的条件的元素。这个过程的时间复杂度为
O( log(maxVal−minVal) )
,其中的
minVal
为矩阵左上角的元素,
maxVal
为矩阵右下角的元素。
好了,接下来就是看看怎么猜了~
这里猜的时候又用到了一次二分法,其目的是找到每行中不大于猜的值
val
的数有多少,然后算出整个矩阵中不大于
val
的数有多少。如果不大于
val
的数大于
k
则返回true,否则返回false。这个过程的时间复杂度为
这个过程的时间复杂度为
O( nlog(n)log(maxVal−minVal) )
。
最后,上代码~
代码
class Solution {
private:
bool guess(vector<vector<int>>& matrix, int k, int val){
for (int i = 0; i < matrix.size(); i++){
int left = 0;
int right = matrix[i].size() - 1;
int ans = 0;
while (left <= right){
int mid = left + (right - left) / 2;
if (matrix[i][mid] <= val){
left = mid + 1;
ans = mid + 1;
}
else{
right = mid - 1;
}
}
k -= ans;
if (k < 1)
return true;
}
return false;
}
public:
int kthSmallest(vector<vector<int>>& matrix, int k) {
int n = matrix.size();
int left = matrix[0][0];
int right = matrix[n - 1][n - 1];
int ans;
while (left <= right){
int mid = left + (right - left) / 2;
if (guess(matrix, k, mid)){
ans = mid;
right = mid - 1;
}
else{
left = mid + 1;
}
}
return ans;
}
};