堆排序是面试笔试涉及到的比较多的排序方法。排序方法有很多,当被问到时不能只会冒泡,我们的目的是掌握所有主流排序方法,吊打面试官。
堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。
百度百科
完全二叉树(complete binary tree)简单定义:除最后一层,其他层节点都达到最大值;最后一层节点向左对齐;(并不要求左节点要大于右节点)
每个结点的值都大(小)于或等于其左右孩子结点的值,称为大(小)顶堆;大小顶堆只是父子节点大小顺序相反,所以我们只讨论大顶堆;

完全二叉树效率很高,堆排序就是根据完全二叉树的特性来进行排序。简单地说,就是将列表中的数据找到最大的作为根节点,比它小的作为左右节点。
由图可算出节点索引特性:
左子节点索引值=父节点索引值*2+1
右子节点索引值=父节点索引值*2+2
算法步骤:
-
根据特性写出递归建堆方法
-
根据建堆方法找出最大值作为堆顶元素(此时堆顶元素在列表首位,后面值的顺序也基本按二叉树性质构建完毕)
-
将列表首位和末尾交换
-
将列表剩余数值重新建堆,最大值与列表倒数第二个交换,如此循环
如图:(底部文字为:通过两次判断找到最大值,实现一次列表值交换。后续动画只展示列表交换)

有几个需要注意的地方:
-
构建堆时为了获取最大值,我们先比较父节点和左子节点的值,然后比较最大节点和右子节点的值,这样对后续构建也更方便,但前面也说了,完全二叉树并不要求左子节点小于右。
-
构建最大堆时不需要从最后一位往前循环,因为子节点最小也是2n+1。比如list=[1,2,3],如果把3的索引2传进去,左子节点索引为5,超出列表范围,进行了无意义的比较。其实只需要把1的索引0传进去就可以了。也就是len(list) //2 - 1,所以图片中从第三个开始算。
-
为什么从后往前循环:
可以从例子来看;对于[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 + 1right_child_node = 2 * current_index + 2if left_child_node < end_index:# 出现更大的值,更新最大索引if list[left_child_node] > list[current_index]:max_index = left_child_nodeif 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 listlist = [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,希望能帮到你!
算法的路上一起努力,你不能改变过去,但你可以改变未来。
本文深入讲解堆排序算法,包括堆的概念、完全二叉树的特性、堆排序的算法步骤及Python实现代码,帮助读者理解并掌握堆排序方法。
2413

被折叠的 条评论
为什么被折叠?



