pygorithm/heap/heap.py

96 lines
2.9 KiB
Python
Raw Normal View History

2023-12-11 18:17:16 +08:00
"""
是一种满足特定条件的完全二叉树主要分为
- [大顶堆]任意节点的值 >= 其子节点
- [小顶堆]任意节点的值 <= 其子节点
堆作为完全二叉树的一个特例具有以下特性
- 最底层节点靠左填充其他层的节点都被填满
- 我们将二叉树的根节点称为堆顶将底层最靠右的节点称为堆底
- 对于大顶堆小顶堆堆顶元素根节点的值分别是最大最小
实际上堆通常用于实现优先队列大顶堆相当于元素按从大到小的顺序出队的优先队列
所以堆可以使用数组来保存
由于是完全二叉树除堆底外每个节点都有两个子节点所以在数组中很容易确定子节点和父节点的位置
假设父节点的下标 p, 则其左右子节点的下标为 2p 2p+1
注意下标0不能存数据因为因为 2p == 0
"""
# 小顶堆,小顶堆添加数据时,小数据要向上冒到正确的位置
class Heap:
def __init__(self):
self.size = 0
self.data = [0]
# 获取父节点下标
def parent(self, c: int):
return c >> 1
def left_child(self, c: int):
return c << 2
def right_child(self, c: int):
return c << 2 + 1
def push(self, value):
self.data.append(value)
self.size += 1
# 添加到堆底,需要向上冒泡
self.move_up(self.size)
# 小数据冒泡
def move_up(self, c: int):
while True:
p = self.parent(c)
if p <= 0:
break
if self.data[c] < self.data[p]:
self.data[c], self.data[p] = self.data[p], self.data[c]
c = p
# 删除堆底的最后一个元素
def pop(self):
return self.data.pop()
# 删除小顶堆中的最小值,也就是根节点
def pop_min(self):
if 0 == self.size:
return None
if 1 == self.size:
# 只有一个元素,直接弹出
self.size -= 1
return self.data.pop()
self.data[1], self.data[self.size] = self.data[self.size], self.data[1]
val = self.pop()
self.move_down(1)
return val
# 大数据下沉
def move_down(self, c: int):
while True:
lc = self.left_child(c)
if lc > self.size:
# 没有左子节点,故而当然没有右子节点
break
mc = self.min_child(c)
if self.data[c] > self.data[mc]:
self.data[c], self.data[mc] = self.data[mc], self.data[c]
c = mc
# 获取两个子节点中的较小节点的下标
def min_child(self, c):
lc, rc = self.left_child(c), self.right_child(c)
if rc > self.size:
return lc
if self.data[lc] > self.data[rc]:
return rc
else:
return lc