堆排序+代码实现

堆排序

堆, 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

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值