参考
如果输入一个数组,让你求中位数,这个好办,排个序,如果数组长度是奇数,最中间的一个元素就是中位数,如果数组长度是偶数,最中间两个元素的平均数作为中位数。
如果数据规模非常巨大,排序不太现实,那么也可以使用概率算法,随机抽取一部分数据,排序,求中位数,近似作为所有数据的中位数。
本文说的中位数算法比较困难,也比较精妙,是力扣第 295 题,要求你在数据流中计算中位数
addNum方法时间复杂度 O(logN),findMedian方法时间复杂度 O(1)。
大顶堆和小顶堆合作解决。大顶堆的堆顶和小顶堆的堆顶的平均数就是我们要的中位数。
两个堆需要算法逻辑正确维护,才能保证堆顶元素是可以算出正确的中位数,我们很容易看出来,两个堆中的元素之差不能超过 1。
依次加入到大顶堆和小顶堆里,维护两个堆数目相同,最终取平均数。
简单说**,想要往large里添加元素,不能直接添加,而是要先往small里添加,然后再把small的堆顶元素加到large中;向small中添加元素同理。**
为什么呢,稍加思考可以想明白,假设我们准备向large中插入元素:
如果插入的num小于small的堆顶元素,那么num就会留在small堆里,为了保证两个堆的元素数量之差不大于 1,作为交换,把small堆顶部的元素再插到large堆里。
如果插入的num大于small的堆顶元素,那么num就会成为samll的堆顶元素,最后还是会被插入large堆中。
反之,向small中插入元素是一个道理,这样就巧妙地保证了large堆整体大于small堆,且两个堆的元素之差不超过 1,那么中位数就可以通过两个堆的堆顶元素快速计算了。
优先队列就是队列不是先进先出,而是带有优先级,使用的也是heap来实现的。
heapq是python自带的优先队列的模块。注意默认最小堆,如果需要最大堆就加上负号。
堆结构分为大顶堆和小顶堆,在heapq中使用的是小顶堆:根节点小于等于孩子节点。
其实拿到堆顶并不需要heapq.nlargest(1, a)而是直接拿a[0]就可以了。确实是,本来就是堆顶,第0个。
heapq.nlargest(1, a)这个函数适用于不是堆的列表直接使用拿到最大或者最小~~相当于再排序了一遍QAQ
注意heapq.heappop 和heapq.heappush之后得到的也是堆。
heapq中创建堆的方法有两种。
heappush(heap, num),先创建一个空堆,然后将数据一个一个地添加到堆中。每添加一个数据后,heap都满足小顶堆的特性。
heapify(array),直接将数据列表调整成一个小顶堆(调整的原理参考上面堆排序的文章,heapq库已经实现了)。
python
import heapq
#向堆中插入元素,heapq会维护列表heap中的元素保持堆的性质
heapq.heappush(heap, item) # push之后还是堆
#heapq把列表x转换成堆
heapq.heapify(x)
#从堆中删除元素,返回值是堆中最小或者最大的元素
heapq.heappop(heap) # heappop面向的是堆,pop之后还是堆
#从可迭代的迭代器中返回最大的n个数,可以指定比较的key------针对列表即可
heapq.nlargest(n, iterable[, key])
#从可迭代的迭代器中返回最小的n个数,可以指定比较的key
heapq.nsmallest(n, iterable[, key])
import heapq
nums = [14, 20, 5, 28, 1, 21, 16, 22, 17, 28]
heapq.nlargest(3, nums) 列表的前n大
# [28, 28, 22]
heapq.nsmallest(3, nums) 列表的前n小
# [1, 5, 14]
a = [100, 200] #随便拿一个列表,heappush就可以得到一个堆了,尽管之前不是堆,也可以一起变成堆
heapq.heappush(a, 5)
heapq.heappush(a, 10)
heapq.heappush(a, 9)
heapq.heappush(a, 3)
heapq.heappush(a, 8)
heapq.heappush(a, 5)
heapq.heappush(a, 3)
heapq.heappop(a)
print(a) # [3, 5, 5, 9, 10, 100, 8, 200]
import heapq
class Solution:
def flowmedian(self , operations ):
self.max_heap = [] ##列表可以直接使用,只要用了heapq.heappush的命令就变成了heapq,除了只提取nlargest外.
self.min_heap = []
self.left = 0
self.right = 0
def input_num(num):
if self.left <= self.right:
heapq.heappush(self.max_heap, -num)
heapq.heappush(self.min_heap, -self.max_heap[0])
heapq.heappop(self.max_heap)
self.left += 1
else:
heapq.heappush(self.min_heap, num)
heapq.heappush(self.max_heap, -self.min_heap[0])
heapq.heappop(self.min_heap)
self.right += 1
def output_mid():
if not self.left and not self.right:
return -1
if self.left > self.right:
return self.min_heap[0]
elif self.right > self.left:
return -self.max_heap[0]
return (-self.max_heap[0] + self.min_heap[0])/2
out = []
for operation in operations:
if operation[0] == 1:
input_num(operation[1])
else:
out.append(output_mid())
return out
laptops = [
{‘name’: ‘ThinkPad’, ‘amount’: 100, ‘price’: 91.1},
{‘name’: ‘Mac’, ‘amount’: 50, ‘price’: 543.22},
{‘name’: ‘Surface’, ‘amount’: 200, ‘price’: 21.09},
{‘name’: ‘Alienware’, ‘amount’: 35, ‘price’: 31.75},
{‘name’: ‘Lenovo’, ‘amount’: 45, ‘price’: 16.35},
{‘name’: ‘Huawei’, ‘amount’: 75, ‘price’: 115.65}
]
cheap = heapq.nsmallest(3, portfolio, key=lambda s: s[‘price’])
expensive = heapq.nlargest(3, portfolio, key=lambda s: s[‘price’])```
~~ sorted(nums, key = lambda x : x[1])
lambda定义匿名函数的语法,首先是lambda关键字,表示我们当下定义的是一个匿名函数。之后跟的是这个匿名函数的参数,我们只用到一个变量x,所以只需要写一个x。如果我们需要用到多个参数,通过逗号分隔,当然也可以不用参数。写完参数之后,我们用冒号分开,冒号后面写的是返回的结果。
square = lambda x: x ** 2
print(square(3))