[Leetcode heap, quickly select, Top-K problem BFPRT] orderly matrix of K smaller elements (378)

topic

Given a nxn matrix wherein each row and each column of elements in ascending order, find the small element of the matrix k.
Please note that it is the first k small element of a sort, but not the first k elements.

Example:

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

返回 13。

Note:
You can assume that the value of k is always valid, 1 ≤ k ≤ n2.

answer

This question and Leetcode 215 notes are very similar, it can get rid of the same few ideas. Wherein BFPRT time complexity of O (N)

But this problem is to enter an ordered array, there should be a better way of it! ? Find a circle did not find time to look at.

Ideas:
1, all the income list, sort, value. O (N · log (N))
2, maintains a stack size k, greater than or equal top of the stack of elements into the negative stack top of the stack is the first k small. O (N · log (K))
. 3, quick selection. Preferably O (N), the worst O (N ^ 2)
. 4, BFPRT. O (N)

Note: N represents a number of elements, i.e. n ^ 2 th

By code is as follows:

import random
from heapq import *

class Solution:
    # 排序
    # def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
    #     l = []
    #     for m in matrix:
    #         l.extend(m)
    #     return sorted(l)[k-1]

    # 快速选择
    # def kthSmallest(self, matrix, k):
    #     nums = []
    #     for m in matrix:
    #         nums.extend(m)

    #     def partition(left, right, base):
    #         temp = nums[base]
    #         nums[base], nums[right] = nums[right], nums[base]  # 基准和末尾元素互换

    #         max_index = left
    #         for i in range(left, right):  # 把所有小于基准的移到左边
    #             if nums[i] < temp:
    #                 nums[max_index], nums[i] = nums[i], nums[max_index]
    #                 max_index += 1

    #         nums[right], nums[max_index] = nums[max_index], nums[right]  # 基准归位
    #         return max_index

    #     def select(left, right, k_smallest):
    #         """在 nums[left, right] 找第k小的元素"""
    #         if left == right:  # 递归终止条件
    #             return nums[left]
    #         pivot_index = random.randint(left, right)  # 随机选择基准(比固定选第一个要好)
    #         base_index = partition(left, right, pivot_index)  # 选第一个(left)为基准,并归位。
    #         if base_index == k_smallest:  # 判断目前已归位的基准,是不是第k_smallest位
    #             return nums[k_smallest]
    #         elif k_smallest < base_index:  # go to 左半部分
    #             return select(left, base_index - 1, k_smallest)
    #         else:  # go to 右半部分
    #             return select(base_index + 1, right, k_smallest)

    #     return select(0, len(nums) - 1, k-1)  # 第k大,是第n-k小

    # 堆
    # def kthSmallest(self, matrix, k):
    #     nums = []
    #     for m in matrix:
    #         nums.extend(m)
    #     hq = []
    #     for x in nums:
    #         if len(hq) < k:
    #             heappush(hq, -x)
    #         elif -x >= hq[0]:
    #             heapreplace(hq, -x)
    #     return -heappop(hq)

    # BFPRT
    def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
        nums = []
        for m in matrix:
            nums.extend(m)

        def getmedian(lis):
            """返回序列lis中位数,在BFPRT中就是求每5个数小组的中位数"""
            begin = 0
            end = len(lis)-1

            sum = begin+end
            mid = sum//2 + sum % 2  # 这个地方加上sum%2是为了确保偶数个数时我们求的是中间两个数的后一个
            return sorted(lis)[mid]

        def BFPRT(nums, left, right):
            """分成每5个数一个小组,并求出每个小组内的中位数"""
            num = right-left+1
            offset = 0 if num % 5 == 0 else 1  # 最后如果剩余的数不足5个,我们也将其分成一个小组,和前面同等对待
            groups = num//5 + offset
            median = []  # 中位数数组
            for i in range(groups):
                begin = left+i*5
                end = begin + 4
                Median = getmedian(nums[begin:min(end, right)+1])
                median.append(Median)
            return getmedian(median)

        def partition(nums, left, right, base):
            """在 nums[left, right] 将基准base归位"""
            temp = nums[base]
            nums[base], nums[right] = nums[right], nums[base]  # 基准和末尾元素互换

            max_index = left
            for i in range(left, right):  # 把所有小于基准的移到左边
                if nums[i] <= temp:  # 要等于啊!这里好坑的说.. 否则通不过[3, 3, 3, 3, 4, 3, 3, 3, 3]  k = 1
                    nums[max_index], nums[i] = nums[i], nums[max_index]
                    max_index += 1
            nums[right], nums[max_index] = nums[max_index], nums[right]  # 基准归位
            return max_index

        def select(nums, left, right, k_smallest):
            """在 nums[left, right] 找第k小的元素"""
            if left == right:  # 递归终止条件
                return nums[left]
            # pivot_index = random.randint(left, right)
            base = BFPRT(nums, left, right)
            base_index = partition(nums, left, right, nums.index(base))  # 选base为基准,并归位。
            if base_index == k_smallest:  # 判断目前已归位的基准,是不是第k_smallest位
                return nums[k_smallest]
            elif k_smallest < base_index:  # 递归左半部分
                return select(nums, left, base_index - 1, k_smallest)
            else:  # 递归右半部分
                return select(nums, base_index + 1, right, k_smallest)
        return select(nums, 0, len(nums) - 1, k-1)  # 第k大,是第n-k小

Guess you like

Origin www.cnblogs.com/ldy-miss/p/12039917.html