对堆排序有疑虑?只需五步,彻底搞定堆排序

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/wangliangluang/article/details/90691037

对堆排序有疑虑?只需五步,彻底搞定堆排序:

在说堆排序之前,首先应该明确一些概念:

一.概念:

1.完全二叉树:
    a.从作为第一层的跟开始,除了最后一层外,第N层的元素的个数,都必须是2的N次方.
    b.最后一行的元素,必须从左边开始放,且中间不能有空格.
    说明:满足这两个特点的二叉树就是完全二叉树.
2.最大堆:
    在满足完全二叉树的条件下,每个父节点的值都大于其子结点的值,一般最大堆用于升序排序.
3.最小堆:
     在满足完全二叉树的条件下,每个父节点的值都小于其子节点的值,一般最小堆用于降序排序.

接下来以提问题方式让你搞懂堆排序:

二.总体思路

把数组中所有的数想象成完全二叉树中的结点,对这个完全二叉树构建最大堆,对数组而言也就是寻找最大值,找到最大值后把这个最大值放在最后,然后再对剩下的数再次构建最大堆(寻找第二大的值),如此重复进行即可

三.具体步骤.(总共需要五大步)

1.构建二叉树(这一步理解就好,并不需要代码实现):

  • 问题一: 其实真的需要构建一个二叉树吗?

        不需要,在每个数组一确定之后,对应的二叉树就已经构建好了,这个二叉树只是人 们想象出来的,
    便于进行排序思考的模型.只不过这个模型需要遵循一定的规则.
    
  • 问题二:那么需要遵循哪些规则呢?

  •   其实就是二叉树中的父节点和子节点在数组中的对应关系.
    规定:父节点如果是第i个,左子节点就是2i+1,右子节点就是2i+2.
    

2.排序1(就是构建最大堆的过程,为什么构建最大堆? 因为要找到数组中的最大值)

    从下往上,从第一个非叶子节点(有子节点)的节点开始,如果该结点大于它的子节点,  则无需交
 换,否则与最大子节点交换.
  • 问题一:那么对应数组中怎么找第一个非叶子结点呢?

       i=arr.length/2-1,这个i就是,那么这个i前面就一定是非叶子节点,所以寻找下一个非叶子
    结点直接n-1即可.
    
  • 问题二:那么为什么i=arr.length/2-1?

  • 这个是根据数学推出来的,我们知道数组中最后一个数一定是最后一个非叶子结点通过2i+1或2i+2算出来的.
         也就是: 2i+1=arr.length-1.
                 2i+2=arr.length-1.
         就是一个中学数学题,求i.发现两个式子都可以求出i=arr.length/2-1.(因为3/2和2/2的结果是一样的,都是1)
    

    *注意:对于整体来说,排序是一个自下而上的过程,而对于单个结点来说,排序又是一个自上而下的过程.
    举例:    93      67    4  5   24   2
    这里3和7交换后,还需要再往下比较,还需要再和4交换.

3,交换.

   满足最大堆特点之后,其实就是找到了数组中的最大值.而且放在数组的第0位.
   我们现在要做的当然就是把最大值放到最后面去
   (对应二叉树中也就  是把跟结点和最后一行的最右边结点进行交换).**

4.排序2.

 这里和排序1稍有区别,因为我们已经排好了最大值,所以不需要最大值参与进来了.
 也就是除了最大值以外,其余所有的数再进行排序,
 构建最大堆.目的就是为了寻找第二个最大值.

5.然后不断重复3和4,直到所有的都排好序即可.

最后附上整个程序:


/**
 * 堆排序
 */
public class HeapSort {
    public static int[] HeapSort(int[] arr){
        if(arr==null||arr.length<1){
            throw new RuntimeException("Invalid Parament");
        }
        //构建最大堆,for实现从下而上的思路.里面的sort排序实现对单个结点从上而下
        for (int i = arr.length/2-1; i >=0 ; i--) {
            sort(arr,i,arr.length);
        }
        //开始交换,这里是对最后一个点的循环,每次把最大值移至最后,把前面的进行排序,重新构建最大堆
        for (int i = arr.length-1; i >0 ; i--) {
            //交换
            swap(arr,i,0);
            //对除去最大值之外的数组重新按最大堆模型进行排序
            //注意这里排序的范围是逐渐减小的
            sort(arr,0,i);
        }
        return arr;
    }

    /**
     * 进行最大堆的构建,也就是排序
     * @param arr 要排序的数组
     * @param i   从哪一个结点开始比较,排序
     * @param length 排序的范围,也就是排序到哪一个结点的范围
     */
    private static void sort(int[] arr, int i,int length) {
        int max;//指向两个子节点中大的结点
        //比较两个子节点,给出最大值
        //这个循环是对父节点的i的循环.2i+2<=length-1.从而对父节点做出限制.
        for (int node= i; node < length/2-1; ) {
            //这里如果最大值排在最后,i不变,还是会把最大值参与进来,所以要用length进行限制.
            if((2*node+2)<length){
                max=arr[2*node+1]>arr[2*node+2] ? 2*node+1 : 2+node+2;
            }else{
                max=2*node+1;
            }
            //如果父节点小于子节点中的最大值,交换
            if(arr[node]<arr[max]){
                swap(arr,node,max);
                //此时max指向交换后的子结点,需要继续向下比较,所以要让i指向交换后的子节点.
                node=max;
            }else {
                //如果父节点大,直接退出循环
                break;
            }
        }
    }
    private static void swap(int[] arr, int i, int max) {
        int flag=arr[i];
        arr[i]=arr[max];
        arr[max]=flag;
    }
    //测试
    public static void main(String[] args) {
        int[] arr={3,5,2,3,1};
        int[] ints = HeapSort(arr);
        for (int anInt : ints) {
            System.out.println(anInt);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/wangliangluang/article/details/90691037