参考:https://labuladong.github.io/algo/2/23/65/
感谢大神
二叉堆(以下简称BH)实际就是存储在数组里的完全二叉树。如果某个父节点存储在数组的 i 位置,那么左子节点存储在 i2 位置,右子节点存储在 i2+1 位置。一般0位置不用,从1开始。
二叉堆可以分为最大堆和最小堆,前者指每个父节点的值大于或等于左右子节点的值,所以根节点的值就是整个堆中值最大的;后者类似,指小于或等于。因此,二叉堆的主要应用是:1)堆排序;2)优先级队列。主要方法只有:1)sink(下沉);2)swim(上浮)
这里写了个python3版本。注释都写了
class MaxPQ:
def __init__(self):
self.size = 0
self.pq = [0]
# 返回当前队列中最大元素
def max(self):
return self.pq[1]
# 交换数组下标i和j的值
def swap(self, i:int, j:int):
self.pq[i], self.pq[j] = self.pq[j], self.pq[i]
# 比较数组下表i和j的值大小
# 这里暂时用int,别的类型也可以
def less(self, i:int, j:int)->bool:
return self.pq[i] < self.pq[j]
# 获取左子节点的下标
def left(self, i:int)->int:
return i * 2
# 获取右子节点的下标
def right(self, i:int)->int:
return i * 2 + 1
# 获得父节点的下标
def parent(self, i:int)->int:
return i // 2
# 上浮第x个元素
def swim(self, x:int):
# 获得父节点下标
f = self.parent(x)
# 没有到堆顶且父节点<x
while x > 1 and self.less(f, x):
self.swap(f, x) # 交换位置
x = f # 继续向上检查
f = self.parent(x)
# 下沉第x个元素
def sink(self, x:int):
while self.left(x) <= self.size:
# 查找左右子节点中较大的值
m = self.left(x)
r = self.right(x)
# 如果右子节点存在且比左子节点大,那么修改m
if r <= self.size and self.less(m, r):
m = r
# 如果x比俩子节点都大,那么下沉结束
if self.less(m, x): break
# 否则交换x和m,继续检查
self.swap(x, m)
x = m
# 插入元素
def insert(self, x):
self.size += 1
# 放到数组末尾
self.pq.append(x)
# 通过swim检查
self.swim(self.size)
# 删除最大元素
def delMax(self):
m = self.pq[1]
self.swap(1, self.size)
del self.pq[self.size]
self.size -= 1
self.sink(1)
return m
def print(self):
print("size:" + str(self.size) + "[" + ",".join([str(i) for i in self.pq]) + "]")
t = MaxPQ()
t.insert(2)
t.print()
t.insert(3)
t.insert(1)
t.print()
t.insert(8)
t.print()
t.insert(6)
t.print()
print(t.delMax())
t.print()
t.insert(-2)
t.print()
'''
输出结果:
size:1[0,2]
size:3[0,3,2,1]
size:4[0,8,3,1,2]
size:5[0,8,6,1,2,3]
8
size:4[0,6,3,1,2]
size:5[0,6,3,1,2,-2]
'''