題目詳情
給定一個 n x n 矩陣,其中每行和每列元素均按升序排序,找到矩陣中第 k 小的元素。
請注意,它是排序後的第 k 小元素,而不是第 k 個不同的元素。
示例:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,
返回 13。
提示:
你可以假設 k 的值永遠是有效的,1 ≤ k ≤ n2 。
——題目難度:中等
基於堆排序的方法
關於優先隊列的使用 可以看看這篇——215. 數組中的第K個最大元素(C++)---基於快排的方法 / 基於堆排序的方法(包含優先隊列的使用總結)
思路大概是:可以限制一個元素大小爲k的大頂堆,當遍歷完一遍matrix後,隊首元素就是第 k 小的元素了(依賴於優先隊列內部的自動排序)。
-使用優先隊列解題代碼如下(精簡版)
class Solution {
public:
int kthSmallest(vector<vector<int>>& matrix, int k) {
priority_queue<int, vector<int>, less<int>> pq;
for(int i = 0; i < matrix.size(); i++)
{
for(int j = 0; j < matrix[0].size(); j++)
{
pq.push(matrix[i][j]);
if (pq.size() > k) pq.pop();
}
}
return pq.top();
}
};
上面的代碼運行起來有點慢,是因爲pq.push(matrix[i][j]); 。
因爲不加判斷 直接加入隊列,這樣優先隊列內部進行的自動排序 次數就會變多,所以運行起來也就越慢。
-下面的改進的代碼(增加判斷語句 以 減少隊列內部進行的自動排序的次數)
class Solution {
public:
int kthSmallest(vector<vector<int>>& matrix, int k) {
priority_queue<int, vector<int>, less<int>> pq;
for(int i = 0; i < matrix.size(); i++)
{
for(int j = 0; j < matrix[0].size(); j++)
{
int temp = matrix[i][j];
if (pq.size() == k && temp < pq.top()) {
pq.pop();
pq.push(temp);
}
else if (pq.size() == k) {
continue;
}
else {
pq.push(temp);
}
}
}
return pq.top();
}
};
←這樣比之前快了一點點。
二分法
解法參考力扣—官方題解裏的二分解法,在進行模擬二分法的時候 可以把 二維數組matrix裏的 全部元素 看成 排好序的 一維數組,這樣可以比較好理解此題的二分解法。
且不用擔心 這裏的二分法 找不到那個待求數,因爲每次循環中都保證了第k小的數在left~right之間,當left==right時,第k小的數即被找出,等於left(right也是一樣的)。
-解題代碼
class Solution {
public:
int kthSmallest(vector<vector<int>>& matrix, int k) {
int l = matrix[0][0];
int r = matrix.back().back();
while (l < r) {
int mid = (l + r) / 2;
int count = search_less_count(matrix, mid);
if (count < k) {
l = mid + 1;
} else {
r = mid;
}
}
return l;
}
int search_less_count(vector<vector<int>>& matrix, int target) {
int cnt = 0;
//從左下角開始查找
int n = matrix.size();
int i = n - 1, j = 0;
while (i >= 0 && j < n) {
if (target >= matrix[i][j]) {
cnt += i + 1;
j++;
} else {
i--;
}
}
return cnt;
}
};