1、基本思想html
堆排序是在选择排序基础上改进的排序,首先创建大根堆(即任意根节点的值均不小于子节点),而后每次取出堆顶元素,从新调整堆,而后再取出堆顶元素,直至最后一个堆元素被取出,则整个排序也就完成了。它的思想就是每次取出堆中的最大值,使其天然成序。算法
2、实现步骤数组
咱们用一个数组来创建堆,期间会用到彻底二叉树的一些性质,好比根节点与子节点索引的关系,叶子节点与非叶子节点分别是哪些。dom
由上面综述可知,堆排序主要有三个步骤函数
1)创建堆 : 创建堆咱们能够当作是,对每个根节点进行调整,每一个根节点都知足堆的性质了,那么整个堆也就创建成功了。详见后面的调整堆。工具
2)输出堆顶 :如今堆已经创建好了,堆顶就是最大值。它在数组中的体现就是,a[0],而数组最后一个元素a[a.length-1]是未知的,将二者交换,这样最大值就到最后去了。在下一次输出时就应该是将a[0]与a[a.length-2]交换了,以此类推。测试
3)调整堆 :采用递归的方法进行调整。一次调整只针对当前的根节点和其子节点。若是子节点大于根节点,则交换之。进行了交换操做的子节点必然不能肯定是否知足堆的性质了,因而在对该子节点进行调整,以此类推。那么,递归的出口呢?咱们知道,叶子节点是不用参与调整的,由于它没有子节点了,不会对后面的堆产生影响。因此在进入递归函数时加一个判断便可。当调整到达叶子节点时,递归结束。ui
3、实现代码spa
随机数组测试工具类 点击这里code
package sort; import sort.util.*; /* 堆排序总体思路: 1.创建大根堆 2.将最后一位与堆顶交换 3.因为此次交换,可能破坏堆的结构,进行调整 时间复杂度:外层循环O(n),内层调整取决于堆的深度log2(n),综上时间复杂度为nlog2(n) 空间复杂度:O(1),仅占据一个交换单元 稳定性: 不稳定,跳跃式交换 */ public class HeapSort implements ISort{ /* 假设如今大根堆已经彻底创建了,交换最后一个元素与堆顶元素,至关于堆顶元素排序完成, 则此时须要重新的根开始向下逐级检查是否知足条件,而后作出调整。 */ private void adjustHeap(int[] a , int parent , int maxIndex) { if(maxIndex == 0) { return;} //堆中只剩一个元素了,不用调整了 if(parent >= 0 && parent <= (maxIndex - 1) / 2 ){ //maxIndex-1 / 2 是非叶节点最大索引。叶子节点不用调整,递归结束 //且限制数组越界,故作此判断 int left = 2 * parent + 1; int right = left + 1; //获取左右孩子的索引 int maxChild = left; //默认左孩子最大 if( right <= maxIndex && a[left] < a[right] ) { //若是右孩子存在且右孩子大于左孩子 maxChild = right; } if(a[maxChild] > a[parent]) { //若是孩子节点大于父节点,须要交换值。不然递归结束。 int t = a[maxChild]; a[maxChild] = a[parent]; a[parent] = t; adjustHeap(a , maxChild , maxIndex); //若是该孩子节点进行了交换,则必须对其进行检查、调整。 } } } public void buildHeap(int[] a) { //从最后一个非叶节点开始从下向上调整,创建堆。最大值必定是从下向上冒的。 for(int i = (a.length - 2) / 2; i >= 0 ; i-- ){ adjustHeap(a , i , a.length - 1); } } //将堆顶元素与堆中的最后元素的值交换,最后一个元素就有序了,堆规模减一 public void sort(int[] a) { buildHeap(a); //创建堆 for(int i = a.length-1 ; i >=1 ; i--){ int t = a[i]; a[i] = a[0]; a[0] = t; //最后一个无序元素与堆顶元素交换 adjustHeap(a , 0 , i-1); //调整堆,刚刚交换出来的那个已经不属于堆中元素了 } } public static void main(String[] args) { int[] array = RandomArrayGenerator.getRandomArray(100 , 30); SortTestHelper.test(new HeapSort() , array); } }
测试结果:
4、总结分析
时间复杂度:O(n log n)
空间复杂度:O(1)
堆排序算法的时间主要用在创建堆和调整堆上面,而调整堆的复杂度是与堆的深度有关的,是log n 。因此适用于记录较多的序列。
本文我的编写,水平有限,若有错误,恳请指出,欢迎讨论分享。