概括
利用大(小)堆顶的根节点最大(小)特性, 进行排序<br>
堆的相关概念
堆一般指的是二叉堆,顾名思义,二叉堆是完全二叉树或者近似完全二叉树
堆的性质
① 是一棵完全二叉树
② 每个节点的值都大于或等于其子节点的值,为最大堆;反之为最小堆。
堆的存储
一般用数组来表示堆,下标为 i 的结点的<font color="#c41230">父结点下标为(i-1)/2</font>;其<font color="#c41230">左右子结点分别为 (2i + 1)、(2i + 2)</font>
堆的操作
在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:<br>① 最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点<br>② 创建最大堆(Build_Max_Heap):将堆所有数据重新排序<br>③ 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算<br>
1. 基本思想
利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。<br>① 将待排序的序列构造成一个最大堆,此时序列的最大值为根节点<br>② 依次将根节点与待排序序列的最后一个元素交换<br>③ 再维护从根节点到该元素的前一个节点为最大堆,如此往复,最终得到一个递增序列<br>
2. 实现逻辑
① 先将初始的R[0…n-1]建立成最大堆,而堆顶是最大元素。<br><br>② 再将堆顶R[0]和无序区的最后一个记录R[n-1]交换,由此得到新的无序区R[0…n-2]和有序区R[n-1]<br><br>③ 循环步骤1和步骤2<br><br>④ 直到无序区只有一个元素为止。<br>
3. 动图演示
4. 复杂度分析
<ul><li>平均时间复杂度:O(nlogn)</li><li>最佳时间复杂度:O(nlogn)</li><li>最差时间复杂度:O(nlogn)</li><li>稳定性:不稳定<br><br>堆排序其实也是一种选择排序,是一种树形选择排序。只不过直接选择排序中,为了从R[1…n]中选择最大记录,需比较n-1次,然后从R[1…n-2]中选择最大记录需比较n-2次。事实上这n-2次比较中有很多已经在前面的n-1次比较中已经做过,而树形选择排序恰好利用树形的特点保存了部分前面的比较结果,因此可以减少比较次数。对于n个关键字序列,最坏情况下每个节点需比较log2(n)次,因此其最坏情况下时间复杂度为nlogn。堆排序为不稳定排序,不适合记录较少的排序。<br></li></ul><br>
5. 实现堆排序<br>
假设给定一个组无序数列{100,5,3,11,6,8,7},带着问题,我们对其进行堆排序操作进行分步操作说明。
1、如何由一个无序序列建成一个最大堆?
1. 利用堆特性:<br><ul><li>下标为 i 的结点的<font color="#c41230">父结点下标为(i-1)/2<br></font></li><li>其<font color="#ff0000">左右子结点分别为 (2i + 1)、(2i + 2)</font></li></ul><br>
①首先我们将数组我们将数组从上至下按顺序排列,转换成二叉树:一个无序堆。<br><font color="#c41230">每一个三角关系都是一个堆,上面是父节点,下面两个分叉是子节点,两个子节点俗称左孩子、右孩子;</font><br>
②转换成无序堆之后,我们要努力让这个无序堆变成最大堆(或是最小堆),即每个堆里都实现父节点的值都大于任何一个子节点的值。
③从最后一个堆开始,即 <font color="#c41230">数据最后一个节点 + 其兄弟节点 + 父节点 </font>组成的堆开始;<br>首先对比左右孩子,最大孩子的值比父节点的值大 -> 父节点与最大孩子节点交换<br>如果发生交换,要检测子节点是否为其他堆的父节点,如果是,递归进行同样的操作。<br>
④循环n次
2、如何在输出堆顶元素之后,调整剩余元素成为一个新的堆?
①首先将堆顶元素 与 最底部位置旳元素交换, n-1
②将数组[0,....,n-1]重新进行最大堆处理
③重复步骤1, 步骤2, 直至数组元素个数为1
6. 代码实现