图解堆排序之5分钟就能学会的堆排序算法

一、什么是堆排序

        对于堆排序,我们首先要了解一下二叉堆的特性:最大堆的堆顶是整个堆中的最大元素;最小堆的堆顶是整个堆中的最小元素。
        以最大堆为例,我们删除最大堆的堆顶(注意:这里不是真的删除,而是跟末尾的节点交换位置),那么二叉堆就会根据自我调整,第2大的元素就会被交换上来,成为最大堆的新堆顶。例如对于下面的最大堆:
在这里插入图片描述
删除堆顶元素18,然后通过自我调整使得第二大元素成为新的堆顶。
在这里插入图片描述
然后删除新的堆顶元素16,继续自我调整,得到如下结果:
在这里插入图片描述
继续删除堆顶元素,直到原本的最大二叉堆变成一个从小到大的有序集合,如下所示:
在这里插入图片描述
由于二叉堆实际是存储在数组中的,因此经过以上操作之后,该数组已经是一个有序数组了。

二、堆排序的步骤:
  1. 把无序数组构建成二叉堆。需要对数组进行从小到大排序,则构建成最大堆;需要从大到小排序,则构建成最小堆。
  2. 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。
三、堆排序的代码实现
/**
     * “下沉”调整
     * @param array     待调整的堆
     * @param parentIndex       要“下沉”的父节点
     * @param length        堆的有效大小
     */
    public static void downAdjust(int[] array,int parentIndex,int length){

        //temp保存父节点值,用于最后的赋值
        int temp=array[parentIndex];
        int childIndex=2*parentIndex+1;
        while (childIndex<length){
            //如果有右孩子,且右孩子大于左孩子的值,则定位到右孩子
            if(childIndex+1<length&&array[childIndex+1]>array[childIndex]){
                childIndex++;
            }
            //如果父节点大于任何一个孩子的值,则直接跳出
            if (temp>=array[childIndex])
                break;
            //无需真正交换,单向赋值即可
            array[parentIndex]=array[childIndex];
            parentIndex=childIndex;
            childIndex=2*childIndex+1;
        }
        array[parentIndex]=temp;
    }

    public static void heapSort(int[] array){

        //把无序数组构建成最大堆
        for (int i=(array.length-2)/2;i>=0;i--){
            downAdjust(array,i,array.length);
        }

        //循环删除堆顶元素,移到集合尾部,调整堆产生新的堆顶
        for (int i=array.length-1;i>0;i--){
            //最后一个元素和第一个元素进行交换
            int temp=array[i];
            array[i]=array[0];
            array[0]=temp;
            //“下沉”调整最大堆
            downAdjust(array,0,i);
        }
    }

堆排序分析: 堆排序是一种不稳定排序算法,其时间复杂度是O(nlogn),并且由于没有开辟新的空间,堆排序的空间复杂度是O(1)。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值