[leetcode] 378. Kth Smallest Element in a Sorted Matrix 解题报告

本文介绍了解决LeetCode上寻找n x n有序矩阵中第K小元素问题的两种方法:一种是通过优先队列实现,利用队列特性从大到小遍历矩阵;另一种是采用二分查找法,通过调整查找范围快速定位目标值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接: https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/

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个小的数, 而数组的特性是从左到右有序, 从上到下有序. 所以这题的算法也可以理解为从大到小遍历数组, 所以很符合优先队列的特性. 这样就可以先把最大的数放到优先队列中去, 然后比最大的数小的数要么在他的左边, 要么在他的上面, 再依次将这两个入队列即可. 考虑到优先队列的特性, 总是最大的数在顶部, 所以我们每次就可以将最大的数出队列, 这样直到找到我们要的数.

另外还有一些小技巧, 优先队列要保存哪些信息? 很显然要利用优先队列的性质, 我们要以元素的值为key, 然后还需要的一个信息是这个元素的位置. 也就是这是三个信息, 不太好保存, 如果是两个的话就可以以一个pair的形式保存到优先队列中去了. 而事实上一个元素的坐标是可以保存成一个数的, 比如(x, y)可以保存成x*n+y, 也就是将二维信息转化为一维, 这是一个非常有用的技巧, 我经常会用到. 还有就是访问过的位置不能再入队列了, 所以我们记录一下访问过的位置. 这个可以由unordered_set来完成, 这个数据结构类似于hashmap, 当然也可以用hashmap了, 都可以在O(1)存取数据. 这样基本的算法就完成了. 时间复杂度为最坏为O(n*log(n)), n为数组全部元素个数

代码如下:

class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        priority_queue<pair<int, int>> que;
        int n = matrix.size(), len = n*n;
        que.push(make_pair(matrix[n-1][n-1], (n-1)*(n+1)));
        unordered_set<int> hash;
        hash.insert((n-1)*(n+1));
        while(!que.empty())
        {
            auto val = que.top();
            int x = val.second/n, y = val.second%n, tem = val.first;
            if(k == len) return tem;
            que.pop();
            len--;
            if(x-1 >= 0 && hash.count((x-1)*n+y)==0)
            {
                hash.insert((x-1)*n+y);
                que.push(make_pair(matrix[x-1][y], (x-1)*n+y));
            }
            if(y-1 >= 0 && hash.count(x*n+y-1)==0) 
            {
                hash.insert(x*n+y-1);
                que.push(make_pair(matrix[x][y-1], x*n + y-1));
            }
        }
        return 0;
    }
};


还有一种比较简洁的做法,就是使用二分法初始将left取最小值, right取最大值,然后每次扫描整个数组查找小于(left+right)/2的元素个数,如果此个数小于k,则将left值变为mid+1,否则right = mid,这样的时间复杂为log(Max-Min)*n*log(n),相比与上题来说这种算法依赖与数组中的最大值和最小值的差,不过对于大的数据来说其表现还是优于上个算法.

代码如下:

class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        int n = matrix.size(), left=matrix[0][0], right=matrix[n-1][n-1];
        while(left < right)
        {
            long mid = (left+right)/2, num = 0;
            for(int i = 0; i < n; i++)
            {
                auto it = upper_bound(matrix[i].begin(), matrix[i].end(), mid);
                num += it - matrix[i].begin();
            }
            if(num < k) left = mid+1;
            else right = mid;
        }
        return left;
    }
};
参考:https://discuss.leetcode.com/topic/52865/my-solution-using-binary-search-in-c





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值