leetcode 378. Kth Smallest Element in a Sorted Matrix

来源:互联网 发布:js动态绑定type 编辑:程序博客网 时间:2024/06/11 10:18

题目描述

Given a n x n matrix where each of the rows and columns are sorted in ascending order, find the kth smallest element in the matrix.

Note that it is the kth smallest element in the sorted order, not the kth distinct element.

Example:

matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,

return 13.
Note:
You may assume k is always valid, 1 ≤ k ≤ n2.

解题思路

本题没有要求时间复杂度,但很明显,按序查找不会是一个合理的答案,解决第K小元素的通常解法,二分查找算一个,最小堆也算一个,快速排序的变种也可以解,但对于本题来说,最后一种明显不适用,因为本题是一个矩阵,最小堆解决本题会简单一些,我们先用这种方法来解决。

最小堆

最小堆的一个性质就是堆顶元素最小,要找到第k小元素,一共对堆顶做k-1次移除就可以了,对于本题来说,我们从第一行/第一列构建初始的最小堆,每次从中移除一个元素,再从下一行/下一列的对应位置加一个元素进入最小堆(思考一下,这样做保证了所有元素按从小到大的顺序加入最小堆,同时不加入过多元素),执行k-1次这个过程,堆顶元素即为所求。
时间复杂度:O(klogn)
具体实现如下:

struct Tuple {  int i;  int j;  int val;  Tuple(int iv,int jv,int valv):i(iv),j(jv),val(valv){};};bool operator<(const Tuple& t1,const Tuple&t2) {    return t1.val>t2.val;}class Solution {public:    int kthSmallest(vector<vector<int>>& matrix, int k) {        int n = matrix.size();        priority_queue<Tuple> q;        for(int i = 0;i<n;i++)q.push(Tuple(i,0,matrix[i][0]));        for(int j = 0;j<k-1;j++) {            auto t = q.top();            q.pop();            if(t.j == n-1) continue;            q.push(Tuple(t.i,t.j+1,matrix[t.i][t.j+1]));        }        return q.top().val;        }};//注意最小堆定义里对小于号的重载//用了auto,不支持C++11的编译器可以改成对应对象的声明就可以了。

二分查找

同样二分查找的变形也可以用来解本题,两种方法的速度差不多,二分查找没有额外的空间消耗。
二分查找:leetcode上关于本题的题解中有一个关于二分查找的概念:search space, 直译过来就是搜索空间,关于搜索空间有两种形式,针对有序序列,search space通常指数组下标,而对于乱序数组,search space通常指元素的取值范围,也就是range,对于本题来说,矩阵本身不是数组的形式,因此我们也采用range作为 search space,二分查找的结束条件就是range的左右两端重合。
对于本题来说,加快运算速度的另一点,是在计算在区间内元素个数的过程中,因为每一行都是有序数组,因此在每一行可以使用二分查找,加快查询速度,此外,一些简单的tricks也可以加快速度,具体看代码。

class Solution {public:    int kthSmallest(vector<vector<int>>& matrix, int k) {int n = matrix.size();        int le = matrix[0][0], ri = matrix[n - 1][n - 1];        int mid = 0;        while (le < ri)        {            mid = le + (ri-le)/2;            int num = 0;            for (int i = 0; i < n; i++)            {                int pos = upper_bound(matrix[i].begin(), matrix[i].end(), mid) - matrix[i].begin();//upper_bound 返回第一个大于key的元素迭代器,lower_bound是大于等于                if(pos==0)                break;//trick 如果发现pos==0 后面的行不需要查了                num += pos;            }            if (num < k)            {                le = mid + 1; //迭代尺度为1,因为不知道le/ri是否是数组中元素            }            else            {                ri = mid;            }        }        return le;    }};

个人更推荐第二种写法,除了空间优势外,也较少使用标准库设施。

0 0
原创粉丝点击