LeetCode Python优秀题解——剑指 Offer 41. 数据流中的中位数

本系列旨在对比不同LeetCode的解题方法效率,占用空间等方面的区别,希望帮助大家能够精进代码水平,用更好的思维与方法去解题。其中的部分解题可能涉及代码的奇技淫巧,我回尽量给大家解释,我也会标注正常的思路至少应该达到何种水准,如果有更好的方法也请大家多多指教!

本系列的资源消耗数据由LeetCode给出,但是LeetCode的评价会有20ms左右的波动,这里列出的只是显示的最优成绩,但是直接复制这里的结果不一定能跑出同样的成绩。
题目:

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

例如,

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

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

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

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

输入:
["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]
示例 2:

输入:
["MedianFinder","addNum","findMedian","addNum","findMedian"]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]
 

限制:

最多会对 addNum、findMedian 进行 50000 次调用。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof

解析:

此题最简单思路就是,不断维护一个有序的数组,每次加入一个数就进行一次排序。这样确实简单优秀且不会超时,但是这显然浪费了大量的算力。

题目要求是只需要中位数即可,并不需要其余的数有序,我们只需要维护两个堆(“大堆”,“小堆”),使得“大堆”的所有数都大于“小堆”,维持这二堆长度相当,返回时取“大堆”中最小的一个数,和“小堆”中最大的一个数就行了。

这里也如同前文一样,若需要提升效率,就需要使用python中的模板库堆(heapq),目前掌握其中的两个函数就好:

heappush(heap, x)   将x压入堆
heappop(heap)  从堆中弹出最小的元素

但是这里的堆都是最小堆,只能返回最小的数,那么如何返回最大的数呢?答案就是在“大”堆中我们正常的存储数据,在“小”堆中,我们存这个数的负数。只要我们维护储存的步骤符合如下的顺序:

(1)若次数是到来的第奇数个数,将此数存入“大堆”,并将“大堆”中最小数x取出,将-x存入“小堆”中。(完成后“小堆”的数据个数比“大堆”多1个)

(2)若次数是到来的第偶数个数,将此数的负数存入“小堆”,并将“小堆”中最小数x取出,将-x存入“大堆”中。(完成后“小堆”的数据个数和“大堆”一样)

取出时比较一下,如大堆和小堆一样长,则取“大堆”中最小的一个数,和“小堆”中最大的一个数求平均。若不是则去“小堆”中最大的即可(需要注意正负号)。

不好解法:

# 1260ms
class MedianFinder:

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


    def addNum(self, num: int) -> None:
        self.now_list.append(num)
        self.len_list+=1
        


    def findMedian(self) -> float:
        
        if self.len_list==0:
            return None
        else:
            self.now_list.sort()
            if self.len_list%2==1:
                return self.now_list[(self.len_list-1)//2]
            else:
                need_num=(self.len_list)//2
                return (self.now_list[need_num]+self.now_list[need_num-1])/2

推荐解法:

# 168ms
from heapq import *
class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        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


 

你可能感兴趣的:(LeetCode,Python优秀题解,数据结构,算法,python,leetcode)