图解排序算法之堆排序

Posted 天专Java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图解排序算法之堆排序相关的知识,希望对你有一定的参考价值。

点击二维码关注

图解排序算法之堆排序

堆排序

  堆排序是利用这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。

  堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

图解排序算法之堆排序

同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子

图解排序算法之堆排序

该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

ok,了解了这些定义。接下来,我们来看看堆排序的基本思想及基本步骤:

堆排序基本思想及步骤

  堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

步骤一 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。

  a.假设给定无序序列结构如下

图解排序算法之堆排序

2.此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。

图解排序算法之堆排序

4.找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。

图解排序算法之堆排序

这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。

图解排序算法之堆排序

此时,我们就将一个无需序列构造成了一个大顶堆。

步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。

a.将堆顶元素9和末尾元素4进行交换

图解排序算法之堆排序

b.重新调整结构,使其继续满足堆定义

图解排序算法之堆排序

c.再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.

图解排序算法之堆排序

后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序

图解排序算法之堆排序

再简单总结下堆排序的基本思路:

  a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;

  b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;

  c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

注:堆排建立最大堆时是从最后一个非叶子节点开始从下往上调整的,而不是从第一个非叶子节点开始。


public class HeapSort {  

public static void main(String[] args) {

HeapSort h=new HeapSort();

}

int a[]={6,8,7,4,9,1};

    public  HeapSort(){   

       heapSort(a);  

    }  

   

    public  void heapSort(int[] a){  

        System.out.println("开始排序"+a.length);  

        int arrayLength=a.length;  

        //循环建堆  

        for(int i=0;i<arrayLength-1;i++){  

            //建堆  

            buildMaxHeap(a,arrayLength-1-i);  

            //交换堆顶和最后一个元素  

            swap(a,0,arrayLength-1-i);  

            System.out.println(Arrays.toString(a));  

        }  

    }  

   

    private  void swap(int[] data, int i, int j) {  

        // TODO Auto-generated method stub  

   

        int tmp=data[i];  

        data[i]=data[j];  

        data[j]=tmp;  

    }  

   

    //对data数组从0到lastIndex建大顶堆  

    private void buildMaxHeap(int[] data, int lastIndex) {  

        // TODO Auto-generated method stub  

        //从lastIndex处节点(最后一个节点)的父节点开始  

   

        for(int i=(lastIndex-1)/2;i>=0;i--){  

            //k保存正在判断的节点  

            int k=i;  

            //如果当前k节点的子节点存在  

            while( k*2+1<=lastIndex){  

                //k节点的左子节点的索引  

                int biggerIndex=2*k+1;  

                System.out.println("i:"+i+"   K:"+k+"   big:"+biggerIndex);

                //如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在  

                if(biggerIndex<lastIndex){  

                    //若果右子节点的值较大  

                    if(data[biggerIndex]<data[biggerIndex+1]){  

                        //biggerIndex总是记录较大子节点的索引  

                        biggerIndex++;  

                    }  

                }  

   

                //如果k节点的值小于其较大的子节点的值  

               if(data[k]<data[biggerIndex]){  

                    //交换他们  

                    swap(data,k,biggerIndex);  

                    //将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值  

                    k=biggerIndex;        

                  

                }else{  

                    break;  

                }

            }

    }

}

运行结果:

最后

  堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)...1]逐步递减,近似为nlogn。所以堆排序时间复杂度一般认为就是O(nlogn)级。


以上是关于图解排序算法之堆排序的主要内容,如果未能解决你的问题,请参考以下文章

图解排序算法之堆排序

图解排序算法之堆排序

图解排序算法之堆排序

排序算法总结(转)

十大经典排序之堆排序,被树耽误的数组

算法排序算法之堆排序