排序算法之堆排序

Posted 帕斯卡精英

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序算法之堆排序相关的知识,希望对你有一定的参考价值。

堆的相关概念

堆一般指的是二叉堆,顾名思义,二叉堆是完全二叉树或者近似完全二叉树

1. 堆的性质

  • 是一棵完全二叉树

  • 每个节点的值都大于或等于其子节点的值,为大根堆;反之为最小堆。


堆示例图

2. 堆的存储

一般用数组来表示堆,下标为 i 的结点的父结点下标为(i-1)/2;其左右子结点分别为 (2i + 1)、(2i + 2)


排序算法之堆排序

堆的存储示例图

3. 堆的操作

在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:

  • 大根堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点

  • 创建大根堆(Build_Max_Heap):将堆所有数据重新排序

  • 堆排序(HeapSort):移除位在第一个数据的根节点,并做大根堆调整的递归运算

堆排序(Heap Sort)

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

1. 基本思想

利用大根堆(小根堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。

  • 将待排序的序列构造成一个大根堆,此时序列的最大值为根节点

  • 依次将根节点与待排序序列的最后一个元素交换

  • 再维护从根节点到该元素的前一个节点为大根堆,如此往复,最终得到一个递增序列

2. 实现逻辑

  • 先将初始的R[0…n-1]建立成大根堆,此时是无序堆,而堆顶是最大元素。

  • 再将堆顶R[0]和无序区的最后一个记录R[n-1]交换,由此得到新的无序区R[0…n-2]和有序区R[n-1],且满足R[0…n-2].keys ≤ R[n-1].key

  • 由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。

  • 直到无序区只有一个元素为止。

3. 动图演示

排序算法之堆排序

堆排序演示-1

堆排序算法的演示。首先,将元素进行重排,以匹配堆的条件。图中排序过程之前简单的绘出了堆树的结构。


排序算法之堆排序

堆排序演示-2

分步解析说明:

实现堆排序需要解决两个问题:

  1. 如何由一个无序序列建成一个堆?

  2. 如何在输出堆顶元素之后,调整剩余元素成为一个新的堆?

假设给定一个组无序数列{100,5,3,11,6,8,7},带着问题,我们对其进行堆排序操作进行分步操作说明。

排序算法之堆排序

无序数列

3.1 创建大根堆

①首先我们将数组我们将数组从上至下按顺序排列,转换成二叉树:一个无序堆。每一个三角关系都是一个堆,上面是父节点,下面两个分叉是子节点,两个子节点俗称左孩子、右孩子;

排序算法之堆排序

无序堆

②转换成无序堆之后,我们要努力让这个无序堆变成大根堆(或是小根堆),即每个堆里都实现父节点的值都大于任何一个子节点的值。

排序算法之堆排序

堆划分

③从最后一个堆开始,即左下角那个没有右孩子的那个堆开始;首先对比左右孩子,由于这个堆没有右孩子,所以只能用左孩子,左孩子的值比父节点的值小所以不需要交换。如果发生交换,要检测子节点是否为其他堆的父节点,如果是,递归进行同样的操作。

④第二次对比红色三角形内的堆,取较大的子节点,右孩子8胜出,和父节点比较,右孩子8大于父节点3,升级做父节点,与3交换位置,3的位置没有子节点,这个堆建成大根堆。


排序算法之堆排序

节点比较交换

⑤对黄色三角形内堆进行排序,过程和上面一样,最终是右孩子33升为父节点,被交换的右孩子下面也没有子节点,所以直接结束对比。

⑥最顶部绿色的堆,堆顶100比左右孩子都大,所以不用交换,至此大根堆创建完成。

排序算法之堆排序

无序堆→大根堆

3.2 堆排序(大根堆调整)

①首先将堆顶元素100交换至最底部7的位置,7升至堆顶,100所在的底部位置即为有序区,有序区不参与之后的任何对比。


排序算法之堆排序

堆顶元素与堆低元素交换

②在7升至顶部之后,对顶部重新做大根堆调整,左孩子33代替7的位置。

排序算法之堆排序

大根堆调整

③在7被交换下来后,下面还有子节点,所以需要继续与子节点对比,左孩子11比7大,所以11与7交换位置,交换位置后7下面为有序区,不参与对比,所以本轮结束,无序区再次形成一个大根堆。


排序算法之堆排序

元素交换

④将大根堆堆顶33交换至堆末尾,扩大有序区;

排序算法之堆排序

元素交换

⑤不断建立大根堆,并且扩大有序区,最终全部有序。


堆排序完成

4. 复杂度分析

  • 平均时间复杂度:O(nlogn)

  • 最佳时间复杂度:O(nlogn)

  • 最差时间复杂度:O(nlogn)

  • 稳定性:不稳定

      堆排序其实也是一种选择排序,是一种树形选择排序。只不过直接选择排序中,为了从R[1...n]中选择最大记录,需比较n-1次,然后从R[1...n-2]中选择最大记录需比较n-2次。事实上这n-2次比较中有很多已经在前面的n-1次比较中已经做过,而树形选择排序恰好利用树形的特点保存了部分前面的比较结果,因此可以减少比较次数。对于n个关键字序列,最坏情况下每个节点需比较log2(n)次,因此其最坏情况下时间复杂度为nlogn。堆排序为不稳定排序,不适合记录较少的排序。

5. 代码实现(C++)


以上是关于排序算法之堆排序的主要内容,如果未能解决你的问题,请参考以下文章

重温基础算法内部排序之堆排序法

重温基础算法内部排序之堆排序法

算法排序之堆排序

排序算法之堆排序

排序算法之堆排序

图解排序算法之堆排序