堆排序
堆, heap, 是二叉树的一种。大根堆有这样的性质——任意一个结点的值比它的左右孩子都要大。
排序思想
1. 先把无序的数组初始化为一个大顶堆
答:将待排元素看作是完全二叉树,物理上用一维数组存储. 从最后一个非叶结点起,将该节点当做根,自上而下进行调整,使之成为一个堆。然后依次对倒数第二个、倒数第三个、直至正数第一个结点进行此操作。
2. 循环将堆顶元素排在末尾, 整个数组就成了升序
Q: 输出堆顶元素后,如何将余下的元素调整为一个堆?
答:将最后一个结点放在原根结点位置上,以它为根进行上述的调整。
复杂度分析
二叉树的层数计算方法,从上往下,1,2,3,4......
耗时的操作有构造初始堆和调整堆两部分。
对深度为h的堆进行自上而下的调整,最多比较次数为2*(h-1)。
在初始化堆的过程中,完全二叉树的高度为h,总的比较次数为
综上,堆排序在复杂度最坏的情况下为O((1)式+(2)式)=O(n*logn)。
代码 py
# by yichu, 2021.05
from typing import List
def heap_adjust(heap: List, heap_size, root_index):
"""
在指定 root_index 的左右孩子均已是大顶推的情况下, 将含 root_index 的整棵树调整为大顶堆.
做法为root节点如果较小,就一直下沉
:param heap: 用数组作存储实现, heap[0] 为树的第一个节点
:param heap_size:
:param root_index:
:return:
"""
child_index = 2 * root_index + 1
while child_index < heap_size:
# 比较左右孩子, 选最大
right_child_index = child_index + 1
if right_child_index < heap_size and heap[right_child_index] > heap[child_index]:
child_index = right_child_index
# 如果父节点的值比最大的孩子要大, 跳出
if heap[root_index] >= heap[child_index]:
break
else:
heap[root_index], heap[child_index] = heap[child_index], heap[root_index]
root_index = child_index
child_index = 2 * root_index + 1
def heap_sort(heap: List) -> List:
"""堆排序, 升序要用大顶堆"""
heap_size = len(heap)
# 1. 从最后一个孩子节点起, [从右到左, 从下到上] 地构建大顶堆
for i in range(heap_size - 1, -1, -1):
heap_adjust(heap, heap_size, i)
# 2. 循环删除堆顶元素,移到列表尾部,调节堆产生新的堆顶
for i in range(heap_size - 1, 0, -1):
heap[0], heap[i] = heap[i], heap[0]
heap_adjust(heap, i, 0)
return heap
if __name__ == "__main__":
# [1, 2, 3, 5, 6, 7, 9, 400]
print(heap_sort([1, 3, 400, 5, 2, 6, 9, 7]))
相关习题
初始序列为[1 8 6 2 5 4 7 3]一组数采用堆排序,当建堆(小根堆)完毕时,堆所对应的二叉树中序遍历序列为:(A)
A.8 3 2 5 1 6 4 7 B.3 2 8 5 1 4 6 7 C.3 8 2 5 1 6 7 4 D.8 2 3 5 1 4 7 6