死磕排序算法之堆排序

堆排序是面试笔试涉及到的比较多的排序方法。排序方法有很多,当被问到时不能只会冒泡,我们的目的是掌握所有主流排序方法,吊打面试官。

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

百度百科

完全二叉树(complete binary tree)简单定义:除最后一层,其他层节点都达到最大值;最后一层节点向左对齐;(并不要求左节点要大于右节点)

每个结点的值都大(小)于或等于其左右孩子结点的值,称为大(小)顶堆;大小顶堆只是父子节点大小顺序相反,所以我们只讨论大顶堆;

 

 

完全二叉树效率很高,堆排序就是根据完全二叉树的特性来进行排序。简单地说,就是将列表中的数据找到最大的作为根节点,比它小的作为左右节点。

由图可算出节点索引特性:

    左子节点索引值=父节点索引值*2+1

    右子节点索引值=父节点索引值*2+2

算法步骤:

  1. 根据特性写出递归建堆方法

  2. 根据建堆方法找出最大值作为堆顶元素(此时堆顶元素在列表首位,后面值的顺序也基本按二叉树性质构建完毕)

  3. 将列表首位和末尾交换

  4. 将列表剩余数值重新建堆,最大值与列表倒数第二个交换,如此循环

如图:(底部文字为:通过两次判断找到最大值,实现一次列表值交换。后续动画只展示列表交换)

堆排序

有几个需要注意的地方:

  1. 构建堆时为了获取最大值,我们先比较父节点和左子节点的值,然后比较最大节点和右子节点的值,这样对后续构建也更方便,但前面也说了,完全二叉树并不要求左子节点小于右。

  2. 构建最大堆时不需要从最后一位往前循环,因为子节点最小也是2n+1。比如list=[1,2,3],如果把3的索引2传进去,左子节点索引为5,超出列表范围,进行了无意义的比较。其实只需要把1的索引0传进去就可以了。也就是len(list) //2 - 1,所以图片中从第三个开始算。

  3. 为什么从后往前循环:

    可以从例子来看;对于[1,2,3,4,7,5,6],我们以3为基准和5,6比较,然后以2为基准和4,7比较,最后1为基准和2,3位置交换后的数比较,这样覆盖了全部数据,同时将最大值逐步送到第一位。

python代码:

# 构建堆# current_index:父节点索引  end_index:构建值索引,超出部分不参与构建def heapify(list, current_index, end_index):    # 存储最大值所在索引    max_index = current_index    # 左右子节点    left_child_node = 2 * current_index + 1    right_child_node = 2 * current_index + 2    if left_child_node < end_index:        # 出现更大的值,更新最大索引        if list[left_child_node] > list[current_index]:            max_index = left_child_node    if right_child_node < end_index:        if list[right_child_node] > list[max_index]:            max_index = right_child_node    # 最大值不是父节点索引值,交换    if current_index != max_index:        list[current_index], list[max_index] = list[max_index], list[current_index]        print("构建:" , list)        heapify(list, max_index, end_index)# 构建最大堆def buildMaxHeap(list):    # 从后往前找最大值,不需要从列表最后一个开始(下面//整除被当成注释了,好尴尬)    for i in range(len(list) // 2 -1, -1,-1):        heapify(list, i, len(list))# 堆排序def heapSort(list):    buildMaxHeap(list)    print("最大堆构建完毕")    for i in range(len(list)-1,0 ,- 1):        list[0], list[i] = list[i], list[0]        print('交换:', list)        heapify(list, 0, i)    return list
list = [1,2,3,4,7,5,6]print(list)res = heapSort(list)print('result:', list)

打印结果:​​​​​​​

      [1, 2, 3, 4, 7, 5, 6]构建: [1, 2, 6, 4, 7, 5, 3]构建: [1, 7, 6, 4, 2, 5, 3]构建: [7, 1, 6, 4, 2, 5, 3]构建: [7, 4, 6, 1, 2, 5, 3]最大堆构建完毕交换: [3, 4, 6, 1, 2, 5, 7]构建: [6, 4, 3, 1, 2, 5, 7]构建: [6, 4, 5, 1, 2, 3, 7]交换: [3, 4, 5, 1, 2, 6, 7]构建: [5, 4, 3, 1, 2, 6, 7]交换: [2, 4, 3, 1, 5, 6, 7]构建: [4, 2, 3, 1, 5, 6, 7]交换: [1, 2, 3, 4, 5, 6, 7]构建: [3, 2, 1, 4, 5, 6, 7]交换: [1, 2, 3, 4, 5, 6, 7]构建: [2, 1, 3, 4, 5, 6, 7]交换: [1, 2, 3, 4, 5, 6, 7]result: [1, 2, 3, 4, 5, 6, 7]

堆排序就讲到这里,我是Yme,希望能帮到你!

算法的路上一起努力,你不能改变过去,但你可以改变未来。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值