pygorithm/heap/heap.py

96 lines
2.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""
堆:是一种满足特定条件的完全二叉树,主要分为
- [大顶堆]:任意节点的值 >= 其子节点
- [小顶堆]:任意节点的值 <= 其子节点
堆作为完全二叉树的一个特例,具有以下特性。
- 最底层节点靠左填充,其他层的节点都被填满。
- 我们将二叉树的根节点称为“堆顶”,将底层最靠右的节点称为“堆底”。
- 对于大顶堆(小顶堆),堆顶元素(根节点)的值分别是最大(最小)的。
实际上,堆通常用于实现优先队列,大顶堆相当于元素按从大到小的顺序出队的优先队列,
所以堆可以使用数组来保存。
由于是完全二叉树,除堆底外,每个节点都有两个子节点,所以在数组中很容易确定子节点和父节点的位置
假设父节点的下标 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