zoukankan      html  css  js  c++  java
  • 算法笔记-堆排

      学习堆排之前先学习一下堆得数据结构,我们常用的一般分为小顶堆和大顶堆,我们以大顶堆为例:

      

     上面从左到右分别是数组,数组转成堆得形式,其实就是一个完全二叉树,而最右边这个可以看到每个节点相对所有子节点来说都是最大的,这就是大顶堆,同理,小顶堆反之(这块不理解的可以看我之前写的一篇文章:https://www.cnblogs.com/gmt-hao/p/14417541.html),而我们这里的堆是数组实现的,数组的子节点和父节点的关系:假设父节点的下标为a, 左子节点大小为2*a+1 ,右子节点下标为2*a+2,假设任一子节点下标为b,而父节点的都是(b-1)/2,带着这些认识我们来看看堆得形成。

    堆排的前提是先将数组构造成一个小顶堆或者大顶堆,我们这里还是以大顶堆为例,那么构造时就会出现两个情况:

      1.当前插入数比父节点大,则需要一直往上交换,直到满足大顶堆规则

      2.当前插入节点比子节点小,则需要往下将子节点中较大的数换上来

    那么我们就先来实现一下这两种情况代码:

    /**
         * 将数组中index下标的元素插入堆
         */
        private static void heapInsert(int[] arr,int index){
            while(arr[index] > arr[(index-1)/2]){
                swap(arr, index, (index-1)/2);
                index = (index-1)/2;
            }
       

    这段代码太简单了,首先看while判断,其实就是看当前节点和父节点大小,若比父节点大则交换,并将当前节点下标index置为父节点下标,继续和父节点比,直到不比父节点大或者没有父节点为止。

    再看当前节点比子节点小时:

     private static void heapify(int[] arr,int index, int size){
            int l = index*2 +1;
            
            //递归方式
            if(l < size) {
                int r= l+1;
                int largest = r > (size -1) || arr[r] < arr[l] ? l : r;
                if(arr[index] < arr[largest]){
                    swap(arr, index, largest);
                    heapify(arr,largest,size);
                }
            }
    
            //循环方式
            while(l<size){
                int r= l+1;
                int largest = r > (size -1) || arr[r] < arr[l] ? l : r;
                if(arr[index] >= arr[largest]){
                    break;
                }
                swap(arr, index, largest);
                index = largest;
                l = index*2 +1;
            }
        }

    这里两个版本,上面的递归方式是我自己理解着去写的,但是后来看到别人的循环方式,可以减少压栈次数,而且更加优雅。其实理解起来都不难,首先找出子节点中较大的值,与自己比较大小,将较大的替换上来,继续往下递归(循环)。

    那么堆排又是怎么做到的呢,其实有了heapInsert我们就可以将数组构造成堆了,那么如何利用堆来进行排序呢:

     如上图所示,先将根节点(最大的)和最后一个交换,这样最后一个节点就是最大的数,再把堆大小缩小一个,再将堆构造成大顶堆(这样最大的数不会动),循环到最后一个数,则可以保证数组有序。代码如下:

    public static void heapSort(int[] arr){
            //数组构造大顶堆
            for(int i=0;i<arr.length;i++){
                heapInsert(arr,i);
            }
    
            int size = arr.length;
            //交换
            for(int j=0;j<arr.length;j++){
                swap(arr, 0, arr.length-j-1);
                heapify(arr,0,--size);
            }
    
        }

    其实这种方式感觉和冒泡排序有点类似(都是每次找一个最大值嘛),只不过每次heapify的复杂度只有O(logN)。

  • 相关阅读:
    Android Studio打包过程和应用安装过程
    MVP模式和Clean模式
    Gradle入门学习---认识buildeTypes和dependencies
    微信小程序官方DEMO解读
    从ListView逐步演变到RecyclerView
    Mac下如何配置环境变量
    Android上滑手势触发和不增加布局层级扩大点击区域
    寻找Fragment的替代品的尝试
    高效的策略模式设计方法
    利用ListView的基本方法实现效果
  • 原文地址:https://www.cnblogs.com/gmt-hao/p/14855451.html
Copyright © 2011-2022 走看看