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

Posted 炫云云

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了378. 有序矩阵中第 K 小的元素相关的知识,希望对你有一定的参考价值。

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

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

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

输入:matrix = [[1,5,9],[10,11,13],[12,13,15]], k = 8
输出:13
解释:矩阵中的元素为 [1,5,9,10,11,12,13,13,15],第 8 小元素是 13

最小堆

由题目给出的性质可知,这个矩阵的每一行均为一个有序数组。问题即转化为从这 n n n 个有序数组中找第 k k k​ 大的数.

首先把第一列构造最小堆,然后依次删除堆中最小值num,并加入矩阵剩余的最小值。剩余的最小值是num 右移一格的值,最小堆(蓝色的值)的变换如下:

class Solution:
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        n = len(matrix) #注:题目中这个矩阵是n*n的,所以长宽都是n

        col = [(matrix[i][0], i ,0) for i in range(n)] #取出第一列的值
        # matrix[i][0]是具体的值,后面的(i,0)是在记录矩阵中的位置,方便每次右移添加下一个
        heapq.heapify(col) #变成一个heap
        for i in range(k - 1): #一共弹k次:这里k-1次,return的时候1次
            num, x, y = heapq.heappop(col) #弹出堆里最小一个
            if y != n - 1: #如果这一行还没被弹完
                heapq.heappush(col , (matrix[x][y+1], x ,y+1 )) #加入num右边的一个值
        return heapq.heappop(col)[0]


二分法

  1. 找出二维矩阵中最小的数 l e f t l e f t left​, 最大的数 right, 那么第 k k k​ 小的数必定在 left ∼ \\sim ​ right 之间.
  2. mid = ( =( =(​​ left + + +​​ right ) / / 2 ) // 2 )//2​​; 在二维矩阵中寻找小于等于 m i d m i d mid​​ 的元素个数 count.
  3. 若这个 count 小于 k k k, 表明第 k k k 小的数在右半部分且不包含 mid ⁡ \\operatorname{mid} mid, 即 l e f t = m i d + 1 , r i g h t = l e f t=m i d+1, r i g h t= left=mid+1,right= right, 保证了第 k k k 小的数在 l e f t ∼ r i g h t l e f t \\sim right leftright 之间
  4. 若这个 count >= k k k, 表明第 k k k 小的数在左半部分且可能包含 mid, 即 l e f t = l e f t , r i g h t = l e f t=l e f t, r i g h t= left=left,right= mid ⁡ \\operatorname{mid} mid, 又保证了第 k k k 小的数在 l e f t ∼ r i g h t l e f t \\sim right leftright之间
  5. 因为每次循环中都保证了第 k k k 小的数在 l e f t ∼ r i g h t l e f t \\sim right leftright之间,当 l e f t = = r i g h t l e f t==r i g h t left==right 时,第 k k k 小的数 即被找出,等于 r i g h t \\mathrm{right} right

注意:这里的 l e f t 、 m i d 、 r i g h t left 、mid 、right leftmidright 是数值,不是索引位置。

count 的计算如下,取 m i d = 8 mid =8 mid=8.

可以这样描述走法:

  • 初始位置在 m a t r i x [ n − 1 ] [ 0 ] matrix[n - 1][0] matrix[n1][0]​(即左下角);
  • 设当前位置为 matrix ⁡ [ i ] [ j ] \\operatorname{matrix}[i][j] matrix[i][j] 。若 matrix ⁡ [ i ] [ j ] ≤ m i d \\operatorname{matrix}[i][j] \\leq m i d matrix[i][j]mid, 则将当前所在列的不大于 m i d m i d mid 的数的数量 (即 i + 1 i+1 i+1 ) 累加到count 中 , 并向右移动, 否则向上移动;
  • 不断移动直到走出格子为止。
class Solution:
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        row = len(matrix)
        col = len(matrix[0])
        left = matrix[0][0]
        right = matrix[row - 1][col - 1]
        while left < right:
            #每次循环都保证第K小的数在left~right之间,当left==right,第k小的数就是left
            mid = left+(right-left)//2
            count = self.findNotBiggerThanMid(matrix, mid, row, col)
            if count<k:
                #第k小的数在右半部分,且不包含mid
                left = mid + 1
            else :
                #第k小的数在左半部分,可能包含mid
                right = mid
        return right

    def findNotBiggerThanMid(self,matrix,mid,row,col):
        #以列为单位找,找到每一列最后一个<=mid的数即知道每一列有多少个数<=mid
        i, j = row - 1, 0
        count =0
        while i >= 0 and j < col:
            if matrix[i][j] <= mid:
                count += i + 1
                j += 1
            else:
                i -= 1
        return count

参考

Krahets - 力扣(LeetCode) (leetcode-cn.com)

以上是关于378. 有序矩阵中第 K 小的元素的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 378. 有序矩阵中第K小的元素 | Python

题目地址(378. 有序矩阵中第 K 小的元素)

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

378. 有序矩阵中第K小的元素(排序或者二分)

378. 有序矩阵中第K小的元素。 ①二分

leetcode.矩阵.378有序矩阵中第K小的元素-Java