leetcode 378. 有序矩阵中第K小的元素
问题描述
给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素。
示例:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,
返回 13。
提示:
你可以假设 k 的值永远是有效的,1 ≤ k ≤ n2 。
问题分析
由于是有序矩阵,那么左上角的数字一定是最小的,而右下角的数字一定是最大的,所以这个是我们搜索的范围,然后我们算出中间数字middle,由于矩阵中不同行之间的元素并不是严格有序的,所以我们要在每一行都查找一下 middle,举一个简单的例子:
[1 2
12 100]
k = 3
那么刚开始 left = 1, right = 100, mid = 50, 遍历完 cnt = 3,此时 right 更新为 50
此时 left = 1, right = 50, mid = 25, 遍历完之后 cnt = 3, 此时 right 更新为 25
此时 left = 1, right = 25, mid = 13, 遍历完之后 cnt = 3, 此时 right 更新为 13
此时 left = 1, right = 13, mid = 7, 遍历完之后 cnt = 2, 此时 left 更新为8
此时 left = 8, right = 13, mid = 10, 遍历完之后 cnt = 2, 此时 left 更新为 11
此时 left = 11, right = 12, mid = 11, 遍历完之后 cnt = 2, 此时 left 更新为 12
循环结束,left 和 right 均为 12,任意返回一个即可。
代码
class Solution {
public:
int kthSmallest(vector<vector<int>>& matrix, int k) {
if(!k)return matrix[0][0];
//以下至少2*2矩阵了
int m = matrix.size();
int left = matrix[0][0],right = matrix[m-1][m-1],middle,count;
while(left < right)
{
middle = left + (right-left)/2;
count = count_less_equal(matrix,middle);
if(count < k)
left = middle+1;
else
right = middle;
}
return right;
}
int count_less_equal(vector<vector<int>>& matrix,int target)
{
int n = matrix.size(),i = n-1,j = 0;//从左下角的元素开始搜索
int ans = 0;
while(i >= 0 && j < n)
{
if(matrix[i][j] <= target)
{//如果当前值比目标值小,说明当前列该元素及以上的元素都比target小,这些元素有i+1个,之后可以向右搜索了
ans += i+1;
++j;
}
else{
//如果当前值比目标值大,则需要从该列向上搜索
--i;
}
}
return ans;
}
};
结果
执行用时:56 ms, 在所有 C++ 提交中击败了88.52%的用户
内存消耗:12 MB, 在所有 C++ 提交中击败了20.00%的用户
代码2
class Solution {
public:
int kthSmallest(vector<vector<int>>& matrix, int k) {
if(!k)return matrix[0][0];
//以下至少2*2矩阵了
int m = matrix.size(), n = matrix[0].size();
priority_queue<int> q;
for(int i = 0; i < m; ++i)
{
for(int j = 0; j < n; ++j)
{
q.emplace(matrix[i][j]);
if(q.size() > k)
q.pop();
}
}
return q.top();
}
};
结果
执行用时:152 ms, 在所有 C++ 提交中击败了16.92%的用户
内存消耗:13.5 MB, 在所有 C++ 提交中击败了20.00%的用户