本文简要总结堆的概念。
更新:2023 / 8 / 20
如果谈到堆排序,那么必然要说说什么是 大根堆
max heap
和 小根堆
min heap
1。
max heap
min heap
那么,我们可以总结出关于 max heap
和 min heap
的结论:
min heap
的根节点是堆中最小值;max heap
的根节点是堆中最大值;堆最重要的两个方法就是 插入
和 删除
方法。
将一个元素插入到堆中,使之依然成为一个堆。
堆在删除元素时,只可以删除根节点,然后使剩下的节点依然成为一个堆。
堆的构建过程其实就是构建一个符合大根堆或者小根堆的完全二叉树,那么就可以使用顺序表来进行存储。
下面举例说明堆的构建过程:
将无序序列 [49, 38, 65, 97, 76, 13, 27, 40]
构建大根堆。
在上面的基础上,向 max heap
插入节点 99
。
99
仍然和 97
存在冲突,再次进行调整,
至此,所有节点都符合堆的特性,99
被成功插入 max heap
。
在上面的基础上,因为只能删除根节点,所以删除 max heap
的 99
。
40
99
,根节点被删除后由最后的节点 40
进行补位。40
并不大于或等于左右子节点。因此,寻找此时左右子节点中的最大值 97
与此时的根节点 40
互换以进行调整,40
并不大于或等于左子节点 76
。因此,寻找此时节点 40
与 76
进行互换,99
被成功从 max heap
删除。堆排序是如何实现的呢?
堆排序其实就是堆的删除过程。每次删除的都是堆的根节点,删除后再进行调整,使得剩下的节点还是堆,然后再删除根节点。重复进行。直至堆中只有一个元素时,便可直接输出。那么删除过程中产生的这个序列即是一个有序序列 2。
同样以上面的例子为例,来看看堆排序是如何实现的:
99
99
的过程可参考上面的 删除
部分的内容。在删除 99
后,剩下的元素构成的堆如下所示:97
76
65
49
40
38
27
13
。[99, 97, 76, 65, 49, 40, 38, 27, 13]
,排序完成。以无序序列 [49, 38, 65, 97, 76, 13, 27, 40,99]
为例,
def maxheap(arr, end):
print('+' * 20 + ' max heapify ')
height = int((end+1)/ 2 - 1)
print(f"{'height':20}: {height}")
for root in range(height, -1, -1):
while True:
child = root * 2 + 1
print(f"{'root':20}: {root}\n{'child':20}: {child}\n{'end':20}: {end}")
if child > end:
print(f"-" * 20 + f' child {child} > end {end}, exit loop')
break
if child+1 <= end and arr[child] < arr[child+1]:
print(f"-" * 20 + f' child+1 {child + 1} <= end {end} and arr[child] {arr[child]} < arr[child+1] {arr[child + 1]}')
child = child + 1
print(f"{'child now':20}: {child}")
if arr[root] < arr[child]:
print(f"-" * 20 + f' arr[root] {arr[root]} < arr[child] {arr[child]}, root {root} child {child}')
arr[root], arr[child] = arr[child], arr[root]
root = child
print(f"-" * 20 + f' arr[root] {arr[root]} > arr[child] {arr[child]}, root {root} child {child} now')
else:
print(f"-" * 20 + f' arr[root] {arr[root]} > arr[child] {arr[child]}, root {root} child {child}, exit loop')
break
return arr
def sortheap(arr, end):
for i in range(end, 0, -1):
arr[0], arr[i] = arr[i], arr[0]
maxheap(arr, i-1)
return arr
if __name__ == '__main__':
a = [49, 38, 65, 97, 76, 13, 27, 40, 99]
print(f"排序前:\n{a}")
print('-' * 100 + ' max heapify ')
a = maxheap(a, len(a)-1)
print(f"大根堆:\n{a}")
print('-' * 100 + ' sort heap ')
a = sortheap(a, len(a)-1)
print(f"堆排序:\n{a}")
输出信息如下所示:
排序前:
[23, 15, 30, 38, 65, 97, 40, 99]
---------------------------------------------------------------------------------------------------- max heapify
++++++++++++++++++++ max heapify
height : 3
root : 3
child : 7
end : 7
-------------------- arr[root] 38 < arr[child] 99, root 3 child 7
-------------------- arr[root] 38 > arr[child] 38, root 7 child 7 now
root : 7
child : 15
end : 7
-------------------- child 15 > end 7, exit loop
root : 2
child : 5
end : 7
-------------------- arr[root] 30 < arr[child] 97, root 2 child 5
-------------------- arr[root] 30 > arr[child] 30, root 5 child 5 now
root : 5
child : 11
end : 7
-------------------- child 11 > end 7, exit loop
root : 1
child : 3
end : 7
-------------------- arr[root] 15 < arr[child] 99, root 1 child 3
-------------------- arr[root] 15 > arr[child] 15, root 3 child 3 now
root : 3
child : 7
end : 7
-------------------- arr[root] 15 < arr[child] 38, root 3 child 7
-------------------- arr[root] 15 > arr[child] 15, root 7 child 7 now
root : 7
child : 15
end : 7
-------------------- child 15 > end 7, exit loop
root : 0
child : 1
end : 7
-------------------- arr[root] 23 < arr[child] 99, root 0 child 1
-------------------- arr[root] 23 > arr[child] 23, root 1 child 1 now
root : 1
child : 3
end : 7
-------------------- child+1 4 <= end 7 and arr[child] 38 < arr[child+1] 65
child now : 4
-------------------- arr[root] 23 < arr[child] 65, root 1 child 4
-------------------- arr[root] 23 > arr[child] 23, root 4 child 4 now
root : 4
child : 9
end : 7
-------------------- child 9 > end 7, exit loop
大根堆:
[99, 65, 97, 38, 23, 30, 40, 15]
---------------------------------------------------------------------------------------------------- sort heap
++++++++++++++++++++ max heapify
height : 2
root : 2
child : 5
end : 6
-------------------- child+1 6 <= end 6 and arr[child] 30 < arr[child+1] 40
child now : 6
-------------------- arr[root] 97 > arr[child] 40, root 2 child 6, exit loop
root : 1
child : 3
end : 6
-------------------- arr[root] 65 > arr[child] 38, root 1 child 3, exit loop
root : 0
child : 1
end : 6
-------------------- child+1 2 <= end 6 and arr[child] 65 < arr[child+1] 97
child now : 2
-------------------- arr[root] 15 < arr[child] 97, root 0 child 2
-------------------- arr[root] 15 > arr[child] 15, root 2 child 2 now
root : 2
child : 5
end : 6
-------------------- child+1 6 <= end 6 and arr[child] 30 < arr[child+1] 40
child now : 6
-------------------- arr[root] 15 < arr[child] 40, root 2 child 6
-------------------- arr[root] 15 > arr[child] 15, root 6 child 6 now
root : 6
child : 13
end : 6
-------------------- child 13 > end 6, exit loop
++++++++++++++++++++ max heapify
height : 2
root : 2
child : 5
end : 5
-------------------- arr[root] 40 > arr[child] 30, root 2 child 5, exit loop
root : 1
child : 3
end : 5
-------------------- arr[root] 65 > arr[child] 38, root 1 child 3, exit loop
root : 0
child : 1
end : 5
-------------------- arr[root] 15 < arr[child] 65, root 0 child 1
-------------------- arr[root] 15 > arr[child] 15, root 1 child 1 now
root : 1
child : 3
end : 5
-------------------- arr[root] 15 < arr[child] 38, root 1 child 3
-------------------- arr[root] 15 > arr[child] 15, root 3 child 3 now
root : 3
child : 7
end : 5
-------------------- child 7 > end 5, exit loop
++++++++++++++++++++ max heapify
height : 1
root : 1
child : 3
end : 4
-------------------- child+1 4 <= end 4 and arr[child] 15 < arr[child+1] 23
child now : 4
-------------------- arr[root] 38 > arr[child] 23, root 1 child 4, exit loop
root : 0
child : 1
end : 4
-------------------- child+1 2 <= end 4 and arr[child] 38 < arr[child+1] 40
child now : 2
-------------------- arr[root] 30 < arr[child] 40, root 0 child 2
-------------------- arr[root] 30 > arr[child] 30, root 2 child 2 now
root : 2
child : 5
end : 4
-------------------- child 5 > end 4, exit loop
++++++++++++++++++++ max heapify
height : 1
root : 1
child : 3
end : 3
-------------------- arr[root] 38 > arr[child] 15, root 1 child 3, exit loop
root : 0
child : 1
end : 3
-------------------- arr[root] 23 < arr[child] 38, root 0 child 1
-------------------- arr[root] 23 > arr[child] 23, root 1 child 1 now
root : 1
child : 3
end : 3
-------------------- arr[root] 23 > arr[child] 15, root 1 child 3, exit loop
++++++++++++++++++++ max heapify
height : 0
root : 0
child : 1
end : 2
-------------------- child+1 2 <= end 2 and arr[child] 23 < arr[child+1] 30
child now : 2
-------------------- arr[root] 15 < arr[child] 30, root 0 child 2
-------------------- arr[root] 15 > arr[child] 15, root 2 child 2 now
root : 2
child : 5
end : 2
-------------------- child 5 > end 2, exit loop
++++++++++++++++++++ max heapify
height : 0
root : 0
child : 1
end : 1
-------------------- arr[root] 15 < arr[child] 23, root 0 child 1
-------------------- arr[root] 15 > arr[child] 15, root 1 child 1 now
root : 1
child : 3
end : 1
-------------------- child 3 > end 1, exit loop
++++++++++++++++++++ max heapify
height : 0
root : 0
child : 1
end : 0
-------------------- child 1 > end 0, exit loop
堆排序:
[15, 23, 30, 38, 40, 65, 97, 99]
参考这里 3
import heapq
a = [49, 38, 65, 97, 76, 13, 27, 40, 99]
print(f"{'排序前':20}: {a}")
print('-' * 50 + ' min heapify ')
heapq.heapify(a)
print(f"{'小根堆':20}: {a}")
print('-' * 50 + ' max heapify ')
newa = [(-i, a[i]) for i in range(len(a))]
heapq.heapify(newa)
print(f"{'新小根堆':20}: {newa}")
maxheap = list()
while newa:
_, s = heapq.heappop(newa)
print(f"_, s: {_}, {s}")
maxheap.append(s)
print(f"{'大根堆':20}: {maxheap}")
输出信息如下所示:
排序前 : [49, 38, 65, 97, 76, 13, 27, 40, 99]
-------------------------------------------------- min heapify
小根堆 : [13, 38, 27, 40, 76, 65, 49, 97, 99]
-------------------------------------------------- max heapify
新小根堆 : [(-8, 99), (-7, 97), (-6, 49), (-3, 40), (-4, 76), (-5, 65), (-2, 27), (-1, 38), (0, 13)]
_, s: -8, 99
_, s: -7, 97
_, s: -6, 49
_, s: -5, 65
_, s: -4, 76
_, s: -3, 40
_, s: -2, 27
_, s: -1, 38
_, s: 0, 13
大根堆 : [99, 97, 49, 65, 76, 40, 27, 38, 13]
以无序序列 [49, 38, 65, 97, 76, 13, 27, 40,99]
为例,
def minheap(arr, start, end):
height = int((end+1)/ 2 - 1)
for root in range(height, -1, -1):
while True:
child = root * 2 + 1
if child > end:
break
if child-1 >= start and arr[child] > arr[child-1]:
child = child-1
if arr[root] > arr[child]:
arr[root], arr[child] = arr[child], arr[root]
root = child
else:
break
return arr
def sortheap(arr, start, end):
for i in range(end, 0, -1):
arr[0], arr[i] = arr[i], arr[0]
minheap(arr, 0, i-1)
return arr
if __name__ == '__main__':
a = [49, 38, 65, 97, 76, 13, 27, 40, 99]
print(f"排序前:\n{a}")
print('-' * 50 + ' min heapify ')
a = minheap(a, 0, len(a)-1)
print(f"小根堆:\n{a}")
print('-' * 50 + ' sort heap ')
arr = sortheap(a, 0, len(a)-1)
print(f"堆排序:\n{a}")
输出信息如下所示:
排序前:
[49, 38, 65, 97, 76, 13, 27, 40, 99]
-------------------------------------------------- min heapify
小根堆:
[13, 27, 38, 40, 76, 65, 97, 49, 99]
-------------------------------------------------- sort heap
堆排序:
[99, 97, 76, 65, 49, 40, 38, 27, 13]
参考这里 3
import heapq
a = [49, 38, 65, 97, 76, 13, 27, 40, 99]
print(f"{'排序前':20}: {a}")
print('-' * 50 + ' min heapify ')
heapq.heapify(a)
print(f"{'小根堆':20}: {a}")
print('-' * 50 + ' min heapify ')
newa = [(i, a[i]) for i in range(len(a))]
heapq.heapify(newa)
print(f"{'新小根堆':20}: {newa}")
minheap = list()
while newa:
_, s = heapq.heappop(newa)
print(f"_, s: {_}, {s}")
minheap.append(s)
print(f"{'小根堆':20}: {minheap}")
输出信息如下所示:
排序前 : [49, 38, 65, 97, 76, 13, 27, 40, 99]
-------------------------------------------------- min heapify
小根堆 : [13, 38, 27, 40, 76, 65, 49, 97, 99]
-------------------------------------------------- min heapify
新小根堆 : [(0, 13), (1, 38), (2, 27), (3, 40), (4, 76), (5, 65), (6, 49), (7, 97), (8, 99)]
_, s: 0, 13
_, s: 1, 38
_, s: 2, 27
_, s: 3, 40
_, s: 4, 76
_, s: 5, 65
_, s: 6, 49
_, s: 7, 97
_, s: 8, 99
小根堆 : [13, 38, 27, 40, 76, 65, 49, 97, 99]
#todo:
关于 Python 标准库 heapq 的 大根堆/最大堆的 API~
浅谈大根堆,小根堆,以及堆排序(python)实现 ↩︎
Python 堆排序 ↩︎
python用heapq模块构建大根堆 ↩︎ ↩︎