面试必备算法|图解堆排序(Python)

堆排序

堆排序的思想

​ 堆排序是用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

​ 以大顶堆为例,现将列表构造成一个大顶堆,此时,整个列表的最大值就是堆顶的值。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序列表了。具体可以总结为下面的三个步骤:

  • 将无序列表构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;

  • 将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;

  • 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个列表有序。

图解堆排序

​ 在了解堆排序之前我们需要先了解堆的概念。对于堆结构我们可以分为大顶堆和小顶堆两种:

  • 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;

  • 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

​ 大顶堆和小顶堆的结构如下所示:

在这里插入图片描述

​ 了解了大顶堆和小顶堆的含义之后,我们开始了解堆排序的过程,首先给出如下的一个无序列表,我们用堆的形式来表示这个列表。

  • 第一步:将无序列表构建成一个大顶堆

在这里插入图片描述

​ 对于这个堆我们的目的是构造出一个大顶堆,构造的过程中我们需要不断的比较每一个堆结构的大小关系,并切把大的元素调整为父节点,对于325三个元素组成的子堆举例:
在这里插入图片描述

​ 经过上述子堆的调整后,原堆的结构如下:

在这里插入图片描述

​ 继续用上述方法调整154三个元素组成的子堆:

在这里插入图片描述

​ 经过本次调整之后,我们发现整个堆的堆顶(5)已经是最大的了,但是调整之后的子堆123又变的混乱了,此时我们再一次对该子堆做调整:

在这里插入图片描述

​ 至此我们把最开始的无序列表构造成了一个大顶堆,接下来就要进行排序的操作了。

  • 第二步:将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;

​ 首先我们将堆顶元素5和末尾元素1做交换:

在这里插入图片描述

​ 进行完本次操作我们可以发现最大的元素5就被放倒了最后的位置,此时我们把5先抛除。

  • 第三步:重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个列表有序。

​ 对于剩下的列表[1342]继续按照第一步的方法构造出大顶堆,然后按照第二步的方法继续找到最大的元素即可,直到排序完成:
在这里插入图片描述
堆排序的性质

最优时间复杂度: O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)
最坏时间复杂度: O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)
稳定性:不稳定

堆排序的代码实现

lst = list(map(int, input().split(',')))


# 创建堆(调整堆)
def heapify(arr, n, i):
    largest = i
    l = 2 * i + 1  # left = 2*i + 1
    r = 2 * i + 2  # right = 2*i + 2

    if l < n and arr[i] < arr[l]:
        largest = l

    if r < n and arr[largest] < arr[r]:
        largest = r

    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr, n, largest)


# 堆排序
def heapSort(arr):
    n = len(arr)

    # Build a maxheap.
    for i in range(n, -1, -1):
        heapify(arr, n, i)

    # 一个个交换元素
    for i in range(n - 1, 0, -1):
        arr[i], arr[0] = arr[0], arr[i]
        heapify(arr, i, 0)
    return arr


heapSort(lst)
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二哥不像程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值