数据结构-堆排序

学会了堆的一些基本功能,让我们了解一下用堆的思想来对数据进行排序的方法——堆排序

梦想成真之前,看上去总是那么遥不可及!


  1. 建堆

对数组int arr[]={ 3 , 5 , 20 , 4 , 16 , 17 }进行排序

要使用堆排序,当然要让它先成为一个堆,所以第一步就是建堆

先说结论:排升序建大堆,排降序建小堆

这里用大堆举例说明,有些人会有疑问,排升序不就是从小到大吗,那建一个小堆一直输出堆顶数据不就可以了吗?

确实!这也是一种方法,但是相对于建大堆的方法,建小堆的时间复杂度较高,让我们来对比一下

例如有N个数要排升序,那么堆的高度h就是logN
建小堆:排序思想就是 每次输出堆顶元素,再重新建堆,反复进行,直到全部输出完。
有N个数,每次输出完之后重新建堆,每次建堆都会向下建堆一次,每次向下建堆的时间复杂度为O(N),所以 建小堆的时间复杂度为O(N2)
建大堆:排序思想就是 每次将堆顶数据和最后一个数据交换,然后观察的数据个数减一,再向下调整,反复进行,直到都交换完。
有N个数,每次交换的时间复杂度为O(1),每次向下调整,最坏的情况是交换h-1次,也就是logN-1,所以 建大堆的时间复杂度为O(N*logN)

可以明显的看到,O(N2)和O(N*logN),比如N=1000,O(N2)是100万,O(N*logN)是1万,所以升序建大堆更优

  1. 对堆进行排序

学会了向下调整,就可以完成堆排序

步骤:(1)忽略已经调整过的元素,将堆顶元素与最后一个元素进行交换

(2)向下调整调整为堆

(1)(2)反复进行

堆在结构上是一个二叉树,而逻辑上就是一个数组,所以排序之后,下标从1到N-1依次输出即可

  1. 代码实现

void HeapSort(int* a, int n)
{
    assert(a);   //a是已经建好堆的数组首元素地址
    int end = n - 1;   //记录最后一个未被调整的元素下标
    while (end > 0)
    {
        Swap(&a[0], &a[end]);     //交换堆顶元素和最后一个未被调整的元素
        AdjustDown(a, end, 0);    //忽略已经被调整的元素,向下调整
        --end;
    }
}
void AdjustDown(HPDataType* a, int size, int parent)
{
    assert(a);
    //先默认左孩子是小的
    int child = parent * 2 + 1;
    while (child < size)
    {
        if ((child + 1) < size && a[child + 1] < a[child])
            child++;//较小的改为右孩子
            //将较小的子节点与父节点交换
            if (a[child] < a[parent])
            {
                Swap(&a[child], &a[parent]);
                //继续向下检查
                parent = child;
                child = parent * 2 + 1;
            }
        else break;
    }
}

好了,这就是堆排序的内容,有问题的地方欢迎评论区留言,如果感觉有帮助,还请三连支持!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

木羽829

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值