1.算法原理及步骤
要搞清楚堆排序,首先就需要明白什么是堆,在堆排序中,堆是一种类似于完全二叉树的结构,那么完全二叉树是什么呢?简单来讲,完全二叉树就是指叶子结点只存在于最后两层的树。而堆排序则是利用这一数据结构进行排序,排序的方法主要分为两种:大顶堆排序和小顶堆排序,其中大顶堆用于排升序,小顶堆用于排降序。
其中,大顶堆的性质是:每一个子堆中(包含一个结点及其左右子结点(若有)),结点值是最大值;
小顶堆的性质是:每一个子堆中(包含一个结点及其左右子结点(若有)),结点值是最小值;
不管是大顶堆还是小顶堆,都不用明确左右子结点间的大小关系。假设结点索引为i(i=0,1,2...),那么其左右结点索引分别为2*i+1和2*i+2,对于大顶堆a[i]≥a[2*i+1]&&a[i]≥a[2*i+2];对于小顶堆有a[i]≥a[2*i+1]&&a[i]≤a[2*i+2]。如下图所示:
需要注意的是,堆排序并非是真正的在树中进行排序,它只是利用了树的性质,将数组中的各元素看做是按序分布在树上的结点,实际上还是在数组内部进行排序的。
以升序为例,先将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
因此,以升序为例,堆排序的步骤分为以下几步:
①构造初始堆,将无序数组构造成大顶堆形式;
假定给定数组如下,其对应的堆结构如图所示:
此时的堆结构并非是大顶堆,因此需要对其进行调整。需要注意的是,叶子结点是没有子结点的,不用对其进行调整,因此就从最后一个非叶子结点开始从下往上调整。那么最后一个非叶子结点怎么找呢?很简单,知道了数组的长度为len,那么最后一个非叶子结点的索引必定就是[len/2]-1(其中[len/2]表示对[len/2]向下取整),即是从图中的结点6(索引为1)开始,此时发现结点6比其右子结点9小,因此就将结点6与结点9交换,如图所示:
然后就继续遍历从下往上,从