堆的概念:
堆是一种完全二叉树,就是除了最后一层之外的其他每一层都被完全填充,并且所有结点都保持向左对齐的树。就像码金字塔的砖块,必须从头到底,从左到右一个一个码,不能空缺。
堆有两种类型:大根堆,小根堆
大根堆:每个结点的值都大于或等于左右孩子结点
小根堆:每个结点的值都小于或等于左右孩子结点
大根堆:
小根堆
大根堆构建流程:
- 先依据构建一个完全二叉树
此处并没有构造实际的二叉树,而是逻辑上:
import math
def print_tree(array):
'''
前空格元素间
170
237
313
4 01
'''
index = 1
depth = math.ceil(math.log2(len(array))) # 因为补0了,不然应该是math.ceil(math.log2(len(array)+1))
sep = ' '
for i in range(depth):
offset = 2 ** i
print(sep * (2 ** (depth - i - 1) - 1), end='')
line = array[index:index + offset]
for j, x in enumerate(line):
print("{:>{}}".format(x, len(sep)), end='')
interval = 0 if i == 0 else 2 ** (depth - i) - 1
if j < len(line) - 1:
print(sep * interval, end='')
index += offset
print()
# Heap Sort
# 为了和编码对应,增加一个无用的0在首位
origin = [0, 30, 20, 80, 40, 50, 10, 60, 70, 90]
total = len(origin) - 1 # 初始待排序元素个数,即n
print_tree(origin)
打印结果:
30
20 80
40 50 10 60
70 90
- 调整完全二叉树中的堆节点
1)度数为2的结点A,如果它的左右孩子结点的最大值比它大的,将这个最大值和该结点交换
2)度数为1的结点A,如果它的左孩子的值大于它,则交换
3)如果结点A被交换到新的位置,还需要和其孩子结点重复上面的过程
def heap_adjust(n, i, array: list):
'''
调整当前结点(核心算法)
调整的结点的起点在n//2,保证所有调整的结点都有孩子结点
:param n: 待比较数个数
:param i: 当前结点的下标
:param array: 待排序数据
:return: None
'''
while 2 * i <= n:
# 孩子结点判断 2i为左孩子,2i+1为右孩子
lchile_index = 2 * i
max_child_index = lchile_index # n=2i
if n > lchile_index and array[lchile_index + 1] > array[lchile_index]: # n>2i说明还有右孩子
max_child_index = lchile_index + 1 # n=2i+1
# 和子树的根结点比较
if array[max_child_index] > array[i]:
array[i], array[max_child_index] = array[max_child_index], array[i]
i = max_child_index # 当前节点调整后,被交换的节点max_child_index所在的子堆,需要判断是否还需要调整
else:
break
3.构建大根堆:
def max_heap(total,array:list):
for i in range(total//2,0,-1):
heap_adjust(total,i,array)
return array
print_tree(max_heap(total,origin))
结果: 90
70 80
40 50 10 60
20 30
构建完大根堆后,开始排序:
- 每次都要让堆顶的元素和最后一个结点交换,然后排除最后一个元素,形成一个新的被破坏的堆。
- 让它重新调整,调整后,堆顶一定是最大的元素。
- 再次重复第1、2步直至剩余一个元素
def sort(total, array:list):
while total > 1:
array[1], array[total] = array[total], array[1]
print_tree(origin)# 堆顶和最后一个结点交换
total -= 1
if total == 2 and array[total] >= array[total-1]:
break
heap_adjust(total,1,array)
print_tree(origin)
return array
print_tree(sort(total,origin))
结果: 10
20 30
40 50 60 70
80 90