【Leetcode】378. Kth Smallest Element in a Sorted Matrix 解题报告

【Leetcode】378. Kth Smallest Element in a Sorted Matrix 解题报告
二维矩阵每行每列都是单调增的,求矩阵中第K小个元素

方法1:使用堆

第K小问题首先想到的应该是堆排序。对于求第K小问题,我们只需要维护一个包含k个元素的最大堆,这样最后堆顶元素就是第k小个元素。
考虑如何维护一个大小为K的堆

  • 首先,当堆的大小小于k时,我们要能在堆的末尾添加新的元素,然后通过上浮操作把新添加的元素调整到适合的为位置
  • 当堆的大小为k时,如果新加入的元素大于堆顶元素,就舍弃。因为堆中当前维护的是最小的k个元素,所以新来一个比这个k个元素最大的还要大,就不然他加入。否则,将堆顶元素换成该元素,并通过下沉的方法将堆调整到满足堆性质的状态
class Solution:
    def kthSmallest(self, matrix, k):
        """
        :type matrix: List[List[int]]
        :type k: int
        :rtype
        """
        #建立一个最大堆
        self.heap = [0]
        for row in matrix:
            for ele in row:
                self.insert_heap(ele,k)
                print(self.heap)

        return self.heap[1]

    def insert_heap(self, number,k):
        # print(self.heap[0],k)
        if self.heap[0] < k:
            self.heap[0] += 1
            self.heap.append(number)
            self.flow_up()
        else:
            if number < self.heap[1]:
                self.heap[1] = number
                self.sink_in()
                
    def flow_up(self):
        flow_node_index = len(self.heap)-1
        parent_index = flow_node_index // 2
        while(parent_index > 0):
            if self.heap[parent_index] < self.heap[flow_node_index]:
                self.heap[parent_index],self.heap[flow_node_index] = self.heap[flow_node_index],self.heap[parent_index]
            else:
                break
            flow_node_index = parent_index
            parent_index = flow_node_index // 2

    def sink_in(self):
        sink_node_index= 1
        while(True):
            left_child = sink_node_index*2
            right_child = left_child +1
            if left_child > self.heap[0]:
                break
            if right_child > self.heap[0]:
                maxchild = left_child
            else:
                maxchild = left_child if self.heap[left_child] > self.heap[right_child] else right_child
            if self.heap[maxchild] < self.heap[sink_node_index]:
                    break
            self.heap[maxchild],self.heap[sink_node_index] = self.heap[sink_node_index],self.heap[maxchild]
            sink_node_index = maxchild

方法2:二分查找

使用二分法进行查找,拿题目给的例子为例

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

首先low =1,high =15,mid = (1+high)//2 = 8,我们找出小于等于8的元素有多少个。
怎么找呢,对于每一行我们可以用二分法查找出小于等于8的元素有多少,然后求和。
比如第一次二分我们得到的值是sum(2,0,0)=2,
如果得到的sum < k,(注意这里不能写<=),说明8这个中间值小了,所以要扩大范围,令low= mid+1,否则low=mid

step1: low = 1, high = 15, mid =8, sum = (2,0,0) = 2
step2: low = 9, high =15,  mid = 12, sum =(3,2,1) = 6
step3: low = 13, high = 15, mid = 14, sum = (3, 3,2) =8

我们的递归终止的条件是 low >= high,此时虽然找到了适合的mid,但这个mid=14,不在数组中,我们继续缩小范围
此时14满足条件但不在数组中,我们要向下搜寻(因为上面我们求的是小于等于xx的个数)

step4: low = 13, high =14,  mid = 13, sum =(3,3,2) = 8
step5: low = 13, high = 13,  mid = 13, sum = (3, 3,2) =8
因为 low == high ,查找结束

直到找到了满足条件的mid值。最终low或者high 或者mid是无限逼近我们要找的那个二维数组中的元素,这里循环能终止,是因为我们用了整除,会导致low和high相等,否则永远也不会停止,只会越来越逼近我们要查找的那个数。所以该方法个人认为还是有一定的局限性。
AC代码

class Solution:
    def kthSmallest(self, matrix, k):
        """
        :type matrix: List[List[int]]
        :type k: int
        :rtype: int
        """
        low = matrix[0][0]
        high = matrix[-1][-1]
        while(low < high):
            mid = (low+high)//2
            if sum(self.FindRightIndex(row, mid) for row in matrix) < k:
                low = mid+1
            else:
                high = mid
        return low

    def FindRightIndex(self,array, element):
        if element < array[0]:
            return 0
        if element >= array[-1]:
            return len(array)
        low = 0
        high = len(array) - 1
        while (low < high):
            # print(low, high)
            mid = (low + high) // 2
            if high - low == 1 and array[low] <= high < array[high]:
                return high
            elif array[mid] <= element:
                low = mid + 1
            else:
                high = mid
        return high

方法3

O(n)的时间复杂度,而且n是二维矩阵的行数,理论依据是这篇论文
http://www.cse.yorku.ca/~andy/pubs/X+Y.pdf
这种方法日后有时间再研究了