本系列旨在对比不同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