• 八大排序之堆排序


    一、基本思想

      堆排序是在选择排序基础上改进的排序,首先建立大根堆(即任意根节点的值均不小于子节点),然后每次取出堆顶元素,重新调整堆,然后再取出堆顶元素,直至最后一个堆元素被取出,则整个排序也就完成了。它的思想就是每次取出堆中的最大值,使其自然成序。

    二、实现步骤

      我们用一个数组来建立堆,期间会用到完全二叉树的一些性质,比如根节点与子节点索引的关系,叶子节点与非叶子节点分别是哪些。

      由上面综述可知,堆排序主要有三个步骤

      1)建立堆      : 建立堆我们可以看成是,对每一个根节点进行调整,每个根节点都满足堆的性质了,那么整个堆也就建立成功了。详见后面的调整堆。

      2)输出堆顶  :现在堆已经建立好了,堆顶就是最大值。它在数组中的体现就是,a[0],而数组最后一个元素a[a.length-1]是未知的,将两者交换,这样最大值就到最后去了。在下一次输出时就应该是将a[0]与a[a.length-2]交换了,以此类推。

      3)调整堆     :采用递归的方法进行调整。一次调整只针对当前的根节点和其子节点。如果子节点大于根节点,则交换之。进行了交换操作的子节点必然不能确定是否满足堆的性质了,于是在对该子节点进行调整,以此类推。那么,递归的出口呢?我们知道,叶子节点是不用参与调整的,因为它没有子节点了,不会对后面的堆产生影响。所以在进入递归函数时加一个判断即可。当调整到达叶子节点时,递归结束。

    三、实现代码

      随机数组测试工具类 点击这里

      

    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);
        }
    }

     测试结果:

    四、总结分析

       时间复杂度:O(n log n)

       空间复杂度:O(1)

      堆排序算法的时间主要用在建立堆和调整堆上面,而调整堆的复杂度是与堆的深度有关的,是log n 。所以适用于记录较多的序列。

      本文个人编写,水平有限,如有错误,恳请指出,欢迎讨论分享。

  • 相关阅读:
    zookeeper记录2(选举模式和ZooKeeper的集群安装)
    端口复用技术简单了解;重用端口;socket复用端口
    java线程池如何合理配置核心线程数?(转)
    数据库锁机制(转)
    mysql左连接锁表_不得不会的mysql锁(转)
    POI: calculated end index (4361) is out of allowable range (4339..4358)
    jdbcTemplate事务管理
    springboot 访问静态资源
    springboot集成持久化框架
    第六章 FreeBSD之配置日期和时间
  • 原文地址:https://www.cnblogs.com/wanghang-learning/p/9209210.html
Copyright © 2020-2023  润新知