对堆排序有疑虑?只需五步,彻底搞定堆排序(详细解释)

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

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

一.概念:

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

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

二.总体思路

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

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

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

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

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

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

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

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

       i=arr.length/2-1,这个i就是,那么这个i前面就一定是非叶子节点,所以寻找下一个非叶子
    结点直接i-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 static void heapSort(int[] nums){
        if(nums==null||nums.length<2) return;
        int len=nums.length/2-1;
        //从第一个非叶子节点开始构建最大堆
        for(int i=len;i>=0;i--){
            sort(nums,i,nums.length-1);
        }
        for(int i=nums.length-1;i>=0;i--){
            //最大堆构建之后,开始依次取最大值(一定放在第0个位置),放到元素末尾
            swap(nums,0,i);
            //对除去最大值之外的数组重新按最大堆模型进行排序
            sort(nums,0,i-1);
        }
    }

    /**
     * 进行最大堆的构建,也就是排序
     * @param nums  排序数组
     * @param index  从哪一个节点开始排序
     * @param len  排序的范围(包括叶子节点的范围)
     */
    public static void sort(int[] nums, int index, int len){
        for(int i=index;i<=len;){
            int max=i;
            //下面是判断最后节点是否有左右子节点,以免越界,因为在第二次排序时最大堆的范围是依次减小的
            if((2*i+2)<=len){
                max=nums[2*i+1]>nums[2*i+2] ? 2*i+1 : 2*i+2;
            }else if((2*i+1)<=len)
                max=2*i+1;
            //当前节点小于左右子节点的最大值,交换
            if(nums[i]<nums[max]){
                swap(nums,i,max);
                i=max;
            }else
                break;
        }
    }
    public static void swap(int[] nums, int left, int right){
        int help=nums[left];
        nums[left]=nums[right];
        nums[right]=help;
    }

测试:[1,3,6,9,10,2]
输出结果:
在这里插入图片描述

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员bling

义父,感谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值