剑指offer 刷题 十七 排序(40 41)

剑指 Offer 40. 最小的k个数
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

剑指offer 刷题 十七 排序(40 41)
这个题我拿到手,第一反应就是一个排序,最后返回前k个就可以。当时想用快速排序,但是发现已经不会写了,尴尬。于是从网上搜索了快速排序的讲解,我发到下面。
快速排序:
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
代码自己写的:

def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        def quicksort(arr, L, R):
            if L >= R: return arr
            left = L
            right = R
            pivot = arr[left]
            while left < right:
                while left < right and arr[right] >= pivot:
                    right -= 1
                arr[left] = arr[right]
                while left < right and arr[left] <= pivot:
                    left += 1
                arr[right] = arr[left]
            arr[left] = pivot
            quicksort(arr, L, right-1)
            quicksort(arr, left+1, R)
        quicksort(arr, 0, len(arr)-1)
        return arr[:k]

注意,在第2个while,一定是>=或者<=,等于号不能少,不然会进入无限的递归循环。
接下来是大佬的思路和代码:
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
大佬代码,同样是快速排序,差距差太多了,大佬的代码简单死了:

class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        def quick_sort(arr, l, r):
            # 子数组长度为 1 时终止递归
            if l >= r: return
            # 哨兵划分操作(以 arr[l] 作为基准数)
            i, j = l, r
            while i < j:
                while i < j and arr[j] >= arr[l]: j -= 1
                while i < j and arr[i] <= arr[l]: i += 1
                arr[i], arr[j] = arr[j], arr[i]
            arr[l], arr[i] = arr[i], arr[l]
            # 递归左(右)子数组执行哨兵划分
            quick_sort(arr, l, i - 1)
            quick_sort(arr, i + 1, r)
        
        quick_sort(arr, 0, len(arr) - 1)
        return arr[:k]

剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
大佬代码:

class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        if k >= len(arr): return arr
        def quick_sort(l, r):
            i, j = l, r
            while i < j:
                while i < j and arr[j] >= arr[l]: j -= 1
                while i < j and arr[i] <= arr[l]: i += 1
                arr[i], arr[j] = arr[j], arr[i]
            arr[l], arr[i] = arr[i], arr[l]
            if k < i: return quick_sort(l, i - 1) 
            if k > i: return quick_sort(i + 1, r)
            return arr[:k]
            
        return quick_sort(0, len(arr) - 1)

剑指 Offer 41. 数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。

剑指offer 刷题 十七 排序(40 41)
下面是我自己写的代码:

class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.li = []

    def addNum(self, num: int) -> None:
        self.li.append(num)
        self.li.sort()

    def findMedian(self) -> float:
        if not self.li: return None
        elif len(self.li) & 1 == 1:
            return self.li[len(self.li)//2]
        else:
            return (self.li[len(self.li)//2] + self.li[len(self.li)//2 - 1]) / 2

剑指offer 刷题 十七 排序(40 41)
下面是大佬的思路和代码:

首先补充一个概念:
小顶堆:堆顶是最小值。
大顶堆:堆顶是最大值。
python只有小顶堆,没有大顶堆,可以通过取负数来实现。
剑指offer 刷题 十七 排序(40 41)
上面这个思路和我的思路是一样的,但是大佬下面是用堆解决的。
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)

剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
以此类推
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
大佬代码,这我真写不出来,不知道用顶堆的概念:

from heapq import *

class MedianFinder:
    def __init__(self):
        self.A = [] # 小顶堆,保存较大的一半
        self.B = [] # 大顶堆,保存较小的一半

    def addNum(self, num: int) -> None:
        if len(self.A) != len(self.B):
            heappush(self.A, num)
            heappush(self.B, -heappop(self.A))
        else:
            heappush(self.B, -num)
            heappush(self.A, -heappop(self.B))

    def findMedian(self) -> float:
        return self.A[0] if len(self.A) != len(self.B) else (self.A[0] - self.B[0]) / 2.0

继续优化代码:
剑指offer 刷题 十七 排序(40 41)

from heapq import *

class MedianFinder:
    def __init__(self):
        self.A = [] # 小顶堆,保存较大的一半
        self.B = [] # 大顶堆,保存较小的一半

    def addNum(self, num: int) -> None:
        if len(self.A) != len(self.B):
            heappush(self.B, -heappushpop(self.A, num))
        else:
            heappush(self.A, -heappushpop(self.B, -num))

    def findMedian(self) -> float:
        return self.A[0] if len(self.A) != len(self.B) else (self.A[0] - self.B[0]) / 2.0

想到之前看算法通关40讲的一道题,我贴在下面。也是应用了小顶堆。
求第K大的元素
剑指offer 刷题 十七 排序(40 41)
剑指offer 刷题 十七 排序(40 41)
永远维护最小值,维护最小值的时间复杂度是O(log2K)
小顶堆拿数据,时间复杂度是O(1).
所以总共是O(NlogK)
剑指offer 刷题 十七 排序(40 41)

上一篇:小程序登陆遇到 ERR_REQUEST_PARAM


下一篇:年终推荐:李宏毅《机器学习》40讲真香