时间:2021-05-19
概述
堆排序是一种树形选择排序,是对直接选择排序的有效改进。
堆的定义如下:具有n个元素的序列(k1,k2,...,kn), 当且仅当满足:
时称之为堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最小项(小顶堆)或最大项(大顶堆)。
若以一维数组存储一个堆,则堆对应一棵完全二叉树,且所有非叶结点(有子女的结点)的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的。
(a)大顶堆序列:(96, 83, 27, 38, 11, 09)
(b)小顶堆序列:(12, 36, 24, 85, 47, 30, 53, 91)
初始时把要排序的n 个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,使之成为一个堆,将堆顶元素输出,得到n 个元素中最小(或最大)的元素。然后对剩下的n-1个元素重新调整使之成为堆,输出堆顶元素,得到n 个元素中次小(或次大)的元素。依此类推,直到最后得到有n个节点的有序序列。称这个过程为堆排序。
步骤&实例
实现堆排序需解决两个问题:
(1)如何将n 个待排序的数建成堆;
(2)输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆。
建堆方法(小顶堆):
对初始序列建堆的过程,就是一个反复进行筛选的过程。
n 个结点的完全二叉树,则最后一个结点是第n/2个结点的子树。
筛选从第n/2个结点为根的子树开始(n/2是最后一个有子树的结点),使该子树成为堆。
之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。
如图建堆初始过程
无序序列:(49, 38, 65, 97, 76, 13, 27, 49)
(a) 无序序列,初始二叉树,97(第8/2=4个结点)为最后一个结点(49)的父结点。
(b) 97>=49,替换位置,接下来对n/2的上一个结点65进行筛选。
(c) 13<=27且65>=13,替换65和13的位置,接下来对38进行替换(都大于它,不需操作),对49进行筛选。
(d) 13<=38且49>=13,替换49和13的位置,49>=27,替换49和27的位置。
(e) 最终得到一个堆,13是我们得到的最小数。
调整堆的方法(小顶堆):
设有m 个元素的堆,输出堆顶元素后,剩下m-1 个元素。将堆底元素送入堆顶,堆被破坏,其原因仅是根结点不满足堆的性质。
将根结点与左、右子树中较小元素的进行交换。
若与左子树交换:如果左子树堆被破坏,则重复方法(2).
若与右子树交换,如果右子树堆被破坏,则重复方法(2).
继续对不满足堆性质的子树进行上述交换操作,直到叶子结点,堆被建成。
调整堆只需考虑被破坏的结点,其他的结点不需调整。
代码实现(Java)
运行代码结合注释与上面的实例步骤进行对比思考。
运行结果:
初始堆开始待调整结点为:array[3] = 97将与子孩子 array[7] = 49 进行比较子孩子比其小,交换位置没有子孩子了,调整结束待调整结点为:array[2] = 65将与子孩子 array[5] = 13 进行比较子孩子比其小,交换位置没有子孩子了,调整结束待调整结点为:array[1] = 38将与子孩子 array[3] = 49 进行比较子孩子均比其大,调整结束待调整结点为:array[0] = 49将与子孩子 array[2] = 13 进行比较子孩子比其小,交换位置继续与新的子孩子进行比较将与子孩子 array[6] = 27 进行比较子孩子比其小,交换位置没有子孩子了,调整结束初始堆结束待调整结点为:array[0] = 97将与子孩子 array[2] = 27 进行比较子孩子比其小,交换位置继续与新的子孩子进行比较将与子孩子 array[6] = 49 进行比较子孩子比其小,交换位置没有子孩子了,调整结束待调整结点为:array[0] = 97将与子孩子 array[1] = 38 进行比较子孩子比其小,交换位置继续与新的子孩子进行比较将与子孩子 array[3] = 49 进行比较子孩子比其小,交换位置没有子孩子了,调整结束待调整结点为:array[0] = 65将与子孩子 array[1] = 49 进行比较子孩子比其小,交换位置继续与新的子孩子进行比较将与子孩子 array[4] = 76 进行比较子孩子均比其大,调整结束待调整结点为:array[0] = 76将与子孩子 array[2] = 49 进行比较子孩子比其小,交换位置没有子孩子了,调整结束待调整结点为:array[0] = 97将与子孩子 array[1] = 65 进行比较子孩子比其小,交换位置没有子孩子了,调整结束待调整结点为:array[0] = 76将与子孩子 array[1] = 97 进行比较子孩子均比其大,调整结束待调整结点为:array[0] = 9797 76 65 49 49 38 27 13PS:堆排序与直接插入排序的区别
直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。
堆排序可通过树形结构保存部分比较结果,可减少比较次数。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
本文实现了八个常用的排序算法:插入排序、冒泡排序、选择排序、希尔排序、快速排序、归并排序、堆排序和LST基数排序首先是EightAlgorithms.java文
java算法之快速排序实现代码摘要:常用算法之一的快速排序算法的java实现原理:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列
本文实现了八个常用的排序算法:插入排序、冒泡排序、选择排序、希尔排序、快速排序、归并排序、堆排序和LST基数排序首先是算法实现文件Sort.h,代码如下:/**
这篇文章主要介绍了Java如何实现八个常用的排序算法:插入排序、冒泡排序、选择排序、希尔排序、快速排序、归并排序、堆排序和LST基数排序,分享给大家一起学习。分
工具类简单明了地总结了java的快速排序,希尔排序,插入排序,堆排序,归并排序五种排序算法,代码中并没有对这几种排序算法的一个说明,关于思想部分希望在自行查阅相