主要实现了四个函数:
add:往堆里加新的值,相当于在list末尾添加值,然后siftup维护大根堆从上到下从大到小
extract:提取堆顶元素,相当于heapq.heappop(heap),提取0位置元素然后用list末尾的元素补到0位置上,siftdown维护大根堆
class Maxheap(object):
def __init__(self, maxsize):
self.maxsize = maxsize #堆的大小
self._elements = [0] * maxsize #初始化堆
self._count = 0 #索引
def add(self, value):
if self._count >= self.maxsize:
raise Exception('full')
self._elements[self._count] = value #放到末尾
self._count += 1 #索引加一,这是当前的下一个索引
self._siftup(self._count - 1) #siftup将当前索引值维护到堆的位置
def extract(self):
if self._count <= 0:
raise Exception('empty')
value = self._elements[0] #记录堆顶值
self._count-=1
self._elements[0] = self._elements[self._count] #末尾移到堆顶
self._siftdown(0) #从上到下维护堆
return value
def _siftup(self, index):
if index > 0:
parent = (index - 1) // 2 #当前索引的父索引
if self._elements[index] > self._elements[parent]: #当前值大于父,需要替换
self._elements[index], self._elements[parent] = self._elements[parent], self._elements[index]
self._siftup(parent) #加入的值换到了父索引位置,继续向上看是不是比上一层的父更大
def _siftdown(self, index):
left = index * 2 + 1 #左子树索引
right = index * 2 + 2 #右子树索引
new_index = index #用一个新索引,后面观察需不需要换
if right < self._count: #有左右子树的情况
if self._elements[left] <= self._elements[index] and self._elements[right] <= self._elements[index]: #当前比左右都大,不用操作
pass
else:
if self._elements[left] >= self._elements[right]:
new_index = left #左边更大,且左边大于当前,准备用左边跟当前索引换
else:
new_index = right
elif left < self._count: #只有左子树
if self._elements[left] >= self._elements[index]:
new_index = left
if new_index != index: #需要换
self._elements[new_index], self._elements[index] = self._elements[index], self._elements[new_index]
self._siftdown(new_index)
topk测试:
#测试代码
import random
seq = list(range(10))
random.shuffle(seq)
heap = MaxHeap(len(seq))
for i in seq:
heap.add(i)
res = []
for i in range(10):
res.append(heap.extract())
heaqp用法:(heapq实现的是小根堆,用大根堆需要转换成相反数)
1. heapq.heappush(heap, item)
将值item插入堆,并执行sift-up维持堆特性(列表末尾元素按规则向上移动)
2. heapq.heappop(heap)
将堆顶元素弹出,并执行sift-down位置堆特性(列表末尾元素赋值给list[0],并按规则向下移动)
3. heapq.heappushpop(heap, item)
相当于heappush和heappop的命令结合,先push item入堆,然后pop堆顶元素
4. heapq.heapreplace(heap, item)
和3的顺序相反,先pop堆顶元素,然后push item入堆
5. heapq.heapify(x)
将列表x转换为heap对象(inplace,线性时间)
6. heapq.merge(*iterables, key=None, reverse=False)
返回: 一个iterable可迭代对象
操作: 将可迭代对象列表*iterables合并, 相当于对一个大文件分解的多个小文件组合排序(具体原理见下方的磁盘排序)
参数:
iterables: 多个输入对象(已经从小到大排序), 例如多个已排序的小文件
key: 用于比较大小的关键字字段, 默认为None
reverse: 默认输出是从小到大, 设置为True后逆序输出
7. heapq.nlargest(n, iterable, key=None)
返回一个列表, 为根据key作为筛选条件从可迭代对象iterable中筛选的最大的n个元素
8. heapq.nsmallest(n, iterable, key=None)
返回一个列表, 为根据key作为筛选条件从可迭代对象iterable中筛选的最小的n个元素