leetcode378. 有序矩阵中第K小的元素(Python3)

该博客介绍了如何解决LeetCode378问题,即在升序排列的矩阵中找到第K小的元素。讨论了两种方法:直接排序(使用heapq进行堆排序,但效率不高)和二分查找(通过计算小于等于目标值的元素个数,利用矩阵特性进行高效查找)。二分查找方法具有较好的时间复杂度O(Nlog(maxx-minn))和空间复杂度O(1)。

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

leetcode378. 有序矩阵中第K小的元素

给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。

请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素。

示例:

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

返回 13。

提示:

你可以假设 k 的值永远是有效的,1 ≤ k ≤ n^2


方法一:直接排序

思路:

最简单粗暴的方法就是直接将matrix中所有的数据进行排序,然后返回第k小的即可。

排序的方法有很多,我这里直接引入heapq来进行堆排序。

排序的时间复杂度很高,不是最好的方法。

代码:

class Solution:
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        import heapq
        nums = []
        n = len(matrix)
        #将matrix中所有数据加入双向队列中
        for i in range(n):
            for j in range(n):
                heapq.heappush(nums,matrix[i][j])
        #弹出前k个最小数的数组
        ans = heapq.nsmallest(k,nums)
        #ans[-1]即为第k小的数
        return ans[-1]

结果:

方法二:二分查找

思路:

矩阵虽然在行和列是有序的,但是我们查找的不是某个值,而是第k小的值,因此对矩阵进行二分查找意义不大。

我们对答案值进行二分查找,首先我们想到答案的范围肯定在矩阵的最小值和最大值之间,我们记为minn和maxx,一个为matrix[0] [0],一个为matrix[n-1] [n-1]。然后我们转换思路,第k小的元素,那么在矩阵中,小于等于它的数的个数为k,或大于k(因为第k小的数在矩阵中可能有重复,导致大于k)

而对于minn和maxx中的值,随着值的增加,这个值在matrix中小于等于它的数的个数一定是递增的(不严格递增),这就构成了有序的序列,我们就可以对这个序列进行二分查找。注意在二分查找的时候,我们要找的是某个值,这个值在matrix中小于等于它的个数为k,或是第一个大于k的,我们定义一个函数calculate(target),来计算matrix中小于等于target的个数。

  • 如果calculate(mid) >= k,这个mid可能是答案,所以right = mid
  • 如果calculate(mid) < k,这个mid不可能是答案,因此left = mid+1,丢弃掉mid

这样最后找到的left=right就是答案,而它对应的calculate值可能等于k也可能大于k(若大于k,是第一个大于k的,对应着答案值在matrix中有多个)。

下面我们来看如何完成calculate函数,我们使用与leetcode240题相似的方法,详细讲解可以看这篇博客
leetcode240. 搜索二维矩阵 II(Python3)

从左下角开始查找,在某个位置x,y时,x表示行序数,y表示列序数。使用cnt计数。

  • 如果该位置值小于等于target,那么说明这个位置这一列上面的所有值都小于target,cnt+=x+1。然后向右走一步,y+=1。
  • 如果该位置值大于target,说明这个位置的值不符合,需要向上走一步缩小值,x-=1。

当x,y越界,遍历结束,返回cnt。

  • 每次calculate的时间复杂度为O(N),二分查找的时间复杂度为O(log(maxx-minn)),所以总的时间复杂度为O(Nlog(maxx-minn))
  • 空间复杂度为O(1)。

代码:

class Solution:
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        n = len(matrix)
        minn = matrix[0][0]
        maxx = matrix[n-1][n-1]

        #这个函数的功能是,返回matrix小于等于target的数的个数
        #从左下角开始找。
        def calculate(target):
            #计数
            cnt = 0
            #初始的位置,x为行数,y为列数
            x = n-1
            y = 0
            while 0 <= x < n and 0 <= y < n:
                if matrix[x][y] <= target:
                    cnt += x+1
                    y += 1
                else:
                    x -= 1
            return cnt
        
        #对数值开始二分查找
        left = minn
        right = maxx
        while left < right:
            mid = left + (right-left)//2
            if calculate(mid) >= k:
                right = mid
            #需要向右压缩,因为calculate(mid)<k,则mid可能不是答案
            #若大于k,则mid可能为答案,因为可能存在多个mid这个值
            else:
                left = mid + 1
        return left

结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值