Algorithm
堆算法分别两类,最大堆算法和最小堆算法。 这里只说最大堆算法,最小堆同理。
最大堆需要满足两个条件:
- 每个父亲节点的值需要大于或者等于其子节点的值。
- 堆是一个完整二叉树
根据最大堆的生成条件,可以知道最大堆有如下性质:
- 编号大于 的元素都没有子节点
- 父亲节点 和子节点 之间满足固定的关系
- 可以确保顶点元素最大
因此,我们考虑使用数组表示最大堆。将堆中元素从上到下,从左到右的填入数组中, 根据最大堆的性质,可以从堆顶元素索引到堆中所有元素。
插入元素
如何将一个元素插入已有最大堆中?
先放到数组最后,然后和父亲节点相比,如果大就交换,否则结束。
删除最大元素
如何在删除最大元素之后仍然可以保持最大堆的性质?
将最后一个元素和堆顶元素交换,然后对第一个元素做下沉操作,即和子节点比较,如果小则和子节点交换,否则结束。
构造最大堆
如何用已有数组构建一个最大堆?
- 在不考虑空间复杂度的情况下,使用新数组将数据一个一个插入即可。
- 原地构造,即不使用其他空间,原地构造最大堆。 可以从 开始向上(堆顶元素),对每个有子节点的元素做下沉处理。
堆排序
如何使用最大堆对数组进行排序?
- 构造最大堆。
- 取出最大堆的堆顶元素作为最大值,将堆中最后一个元素和堆顶元素交换,对堆顶元素做下沉处理。
- 循环第二步直到取出所有元素。
Implements
class MaxHeap:
def __init__(self):
self.nums = []
def father(self, pos: int):
"""Find `pos` element's father element's pos."""
return math.floor((pos - 1) / 2)
def left_child(self, pos: int):
"""Find `pos` element's left child element's pos."""
return (pos + 1) * 2 - 1
def right_child(self, pos: int):
"""Find `pos` element's right child element's pos."""
return (pos + 1) * 2
def insert(self, num: int):
"""Insert `num` to max heap."""
self.nums.append(num)
p = len(self.nums)
while num > self.nums[self.father(p)]:
self.nums[p] = self.nums[self.father(p)]
self.nums[self.father(p)] = num
p = self.father(p)
def pop(self):
"""Pop max element of the max heap."""
num = self.nums[0]
self.nums[0] = self.nums[-1]
self.down(0)
def down(self, pos: int, last_pos=None):
"""Sink `pos` element down to the right place."""
if not last_pos:
last_pos = len(self.nums)
def find_max_pos(pos: int):
left, right = self.left_child(pos), self.right_child(pos)
if left < last_pos and right < last_pos:
max_pos = left if self.nums[left] > self.nums[right] else right
elif left < last_pos:
max_pos = left
elif right < last_pos:
max_pos = right
else:
max_pos = pos
return max_pos
max_pos = find_max_pos(pos)
while self.nums[pos] < self.nums[max_pos]:
t = self.nums[pos]
self.nums[pos] = self.nums[max_pos]
self.nums[max_pos] = t
pos = max_pos
max_pos = find_max_pos(pos)
def sort(self):
# after sort, not a max heap anymore
def swap(pos_a, pos_b):
t = self.nums[pos_a]
self.nums[pos_a] = self.nums[pos_b]
self.nums[pos_b] = t
p = len(self.nums) - 1
while p > 0:
swap(0, p)
self.down(0, p)
p -= 1
def __str__(self):
return str(self.nums)
def construct_max_heap(nums: List[int]):
max_heap = MaxHeap()
for n in nums:
max_heap.insert(n)
return max_heap
def construct_max_heap_inplace(nums: List[int]):
max_heap = MaxHeap()
max_heap.nums = nums
for i in range(len(nums) // 2 - 1, -1, -1):
max_heap.down(i)
return max_heap应用
Question?
构造最大堆时,为什么要从 开始向上(堆顶)进行下沉,而不是从堆顶向下?
只有在除堆顶之外节点都满足最大堆性质时,从能成功进行下沉操作。