LeetCode/solutions/378. Kth Smallest Element in a Sorted Matrix.md

82 lines
3.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# [378. Kth Smallest Element in a Sorted Matrix](https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/)
# 思路
这道题让我们求各行各列分别有序的矩阵中第K小的元素。难点在于矩阵只是局部各行或各列有序整体可能是无序的。
由于是有序的,所以很容易想到可以用二分查找解此题。由于一般的二分都是对数组的下标二分,所以很容易陷入这个定势思维中去。
这里我们需要对元素二分而不是元素的下标二分:
我们很容易知道整个矩阵的最大值high和最小值low分别是右下角和左上角元素所以最终的结果就是在区间[low, high]内。
设`mid = (low+high)/2`那么该如何更新low和high呢我们可以计算有多少个设为count数小于等于mid那么
* 若`count < k`说明不到k个数小于等于mid即最终结果肯定大于mid所以更新`low = mid + 1`
* 否则说明至少k个数小于等于mid即最终结果肯定不大于mid所以更新`high = mid`**注意这里没有减一**)。
所以问题的关键就在于如何计算有多少个数小于等于mid分为两个思路
## 思路一
我们一行一行或者一列一列地遍历矩阵计算每一行有多少个数小于mid最后累加起来就可以了
对于每一行因为是有序的所以我们可以用二分查找的思路计算有多少个数小于mid可直接使用STL中的`upper_bound`函数
* `upper_bound(first, last, val)`返回有序数组或容器的`[first, last)`范围内第一个大于val的元素的位置(指针或者迭代器);
> 而`lower_bound(first, last, val)`返回有序数组或容器的`[first, last)`范围内第一个大于或等于val的元素的位置;
总的时间复杂度即O(nlogn * logM)其中M等于最大值与最小值的差值
## 思路二
思路一只利用了行有序或只利用了列有序所以还有优化的空间
我们从左下角出发或者从右上角出发类似
* 若当前值设下标为i,j小于等于mid因为当前值正上方的值更小些所以累加上这些值的个数`count += (i+1)`然后往右移j++进入下一循环
* 否则即当前值大于mid那我们只需上移i--即可
由于我们只会右移或者上移所以移动步数为2n即线性复杂度所以总的时间复杂度为O(n * logM)其中M等于最大值与最小值的差值
# C++
## 思路一
``` C++
class Solution {
private:
// 记录有多少数小于等于target
int Count_No_Greater(const vector<vector<int>>& matrix, int target){
int n = matrix.size(), count = 0;
for(int i = 0; i < n; i++)
count += upper_bound(matrix[i].begin(),
matrix[i].end(), target) - matrix[i].begin();
return count;
}
public:
int kthSmallest(vector<vector<int>>& matrix, int k) {
int n = matrix.size();
int low = matrix[0][0], high = matrix.back().back();
while(low < high){
int mid = (high - low) / 2 + low;
// count记录有多少数小于等于mid
int count = Count_No_Greater(matrix, mid);
if(count < k) low = mid + 1; // 不到k个数小于等于mid
else high = mid; // 至少k个数小于等于mid
}
return low;
}
};
```
## 思路二
仅`Count_No_Greater`函数与思路一不同
``` C++
// 记录有多少数小于等于target
int Count_No_Greater(const vector<vector<int>>& matrix, int target){
int n = matrix.size(), count = 0;
int i = n - 1, j = 0;
while(i >= 0 && j < n){
if(matrix[i][j] > target) i--;
else{
count += (i + 1);
j++;
}
}
return count;
}
```