一、题目
二、题解
这个矩阵是一个行和列都递增的矩阵,但每行的最后一个元素是不一定大于下一行第一个元素的,题目要求时间复杂度低于O(n²),如果不对时间复杂度进行限制,本题可以通过优先级队列解决TopK问题、排序、快速选择等方法解决,题目描述的矩阵中[0][0]下标元素一定是最小值,[n-1][n-1]元素一定是最大值,我们可以通过二分从这最小值与最大值之间寻找,当枚举第一个mid的时候,我们可以遍历整个矩阵每一行通过二分查找小于等于mid 的个数,统计出矩阵中小于等于mid的个数。根据小于等于答案的元素个数一定是大于等于k的,小于答案的值的个数一定是小于k的。对于上面统计小于等于mid的元素个数时间复杂度是O(n*logn), 但是根据题目矩阵的特性,可以将他优化到线性的复杂度,对于每一次二分所选取的mid值,我们都可以将矩阵划分为左上和右下,所有左上的元素就是小于等于mid的元素个数,如下图,根据这样的性质,我们在统计左上区域的元素个数时,我们只需要从最后一行的0下标开始寻找:如果当前行的0下标是大于mid,就让i--,看上一行,此时如果这一行第一个元素小于等于mid则说明[0 - i]这些元素都是小于等于mid,这些元素的个数就是i + 1, 然后我们接着让j从这一行0下标往后遍历,如果[i][j] <= mid说明这一列都满足,如果此时[i][j] 大于mid重复上述操作i--,继续去寻找直达i到达0或者j到达n
class Solution {
public:
int check(vector<vector<int>>& matrix, int t, int k) {
int cnt = 0;
for (int i = 0; i < matrix.size(); i++) {
int l = 0, r = matrix.size() - 1;
while (l < r) {
int mid = l + (r - l + 1) / 2;
if (matrix[i][mid] <= t) l = mid;
else r = mid - 1;
}
cnt += matrix[i][l] <= t ? l + 1 : 0;
}
return cnt;
}
int kthSmallest(vector<vector<int>>& matrix, int k) {
int l = matrix[0][0], n = matrix.size(), r = matrix[n - 1][n - 1];
while (l < r) {
int mid = l + (r - l) / 2;
cout << mid << endl;
if (check(matrix, mid) >= k) r = mid;
else l = mid + 1;
}
return r;
}
};
优化:
class Solution {
public:
// int check(vector<vector<int>>& matrix, int t, int k) {
// int cnt = 0;
// for (int i = 0; i < matrix.size(); i++) {
// int l = 0, r = matrix.size() - 1;
// while (l < r) {
// int mid = l + (r - l + 1) / 2;
// if (matrix[i][mid] <= t) l = mid;
// else r = mid - 1;
// }
// cnt += matrix[i][l] <= t ? l + 1 : 0;
// }
// return cnt;
// }
int check(vector<vector<int>>& matrix, int mid) {
int i = matrix.size() - 1, j = 0, cnt = 0;
while(i >= 0 && j < matrix.size()) {
if (matrix[i][j] <= mid) {
cnt += i + 1;
j++;
} else {
i--;
}
}
return cnt;
}
int kthSmallest(vector<vector<int>>& matrix, int k) {
int l = matrix[0][0], n = matrix.size(), r = matrix[n - 1][n - 1];
while (l < r) {
int mid = l + (r - l) / 2;
cout << mid << endl;
if (check(matrix, mid) >= k) r = mid;
else l = mid + 1;
}
return r;
}
};