问题描述:
给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第k小的元素。
请注意,它是排序后的第k小元素,而不是第k个元素。
示例:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,
返回 13。
说明:
你可以假设 k 的值永远是有效的, 1 ≤ k ≤ n2 。
基本思路:
这里我们引入一个虚拟数组的概念:
我们的二分法并不基于我们现存的数组。我们只是利用我们现存的数组设定left和right.
而我们的mid则是基于我们自己设定的标准,他并不一定在这个数组中。
AC代码:
class Solution {
public:
int Count(vector<vector<int>> &m, int target) {
int count = 0;
int n = m.size();
// 利用基本已经排好序的矩阵的性质来统计数据
for (int i = 0; i < n; ++i) {
int j = n - 1;
while (j >= 0) {
if (m[j][i] <= target) {
count += j + 1;
break;
} else {
--j;
}
}
}
return count;
}
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 mid = left; // 防止left和right相等的时候,进不去循环
while (left < right) {
mid = left + (right - left) / 2; // mid不再是索引,而是实打实的元素值
int count = Count(matrix, mid); // 在数组中寻找比mid小的所有元素个数
// 夹逼法求得符合要求的元素
if (count >= k) right = mid; // 说明mid过大
else left = mid + 1;
}
return left; // 注意这里是返回left或者right而不是返回mid
}
};
其它经验:
注意夹逼二分的时候返回的是left和right的值,而不是mid的值。