作者:小树苗渴望变成参天大树
作者宣言:认真写好每一篇博客
作者gitee:gitee
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!
前言
今天我们来根据前面所讲的知识点来讲一下堆排序,我们前面讲了堆的一些性质,再本篇会继续用到,并且我会带大家证明那种算法的时间复杂度比较高,接下来我们开始进入正文
一、堆排序
在之前堆的创建中,我们都是把数据一个个插入进数组,采取向上调整算法,但是在排序的时候,我们是已经有了数组,在进行排序,我们根据堆的性质,堆顶的数据不是最大的就是最小的,假设我们排升序,我们是建什么堆合适呢?
我们来看一下建小堆会出现什么情况,是不是第一个元素是整个数组里面的最小的,那升序最小已经在第一位了,我们是不是要把第二位到第n位重新建堆,选出第二小的,但是这种建堆方式就破坏了原本父亲和孩子之间的关系,所以排升序我们需要建大堆,那么第一位就是最大的数,和最后一位交换,然后就使用使用向下调整算法把第一位到第n-1位建堆,就完成排序了,
第一种:先把数组里面的数建成一个大堆
我们采用向上调整算法,假设每一个数都是插入进来的,建完堆,就交换在采取向下调整算法来堆最大数前面的数进行调整看代码:
void HeapSort(int* a, int n)
{
for(int i=0;i<n;i++)//建堆
{
AdjustUp(a,i);
}
int end = n - 1;
while (end >0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
--end;
}
}
这一步是堆的插入和删除特别像,不会的可以先去看看我写的关于堆相关的博客
第二种:
我们从第一个非叶子结点开始采取向下调整算法
我们来看代码:
void HeapSort(int* a, int n)
{
for (int i = (n - 1 - 1) / 2; i >= 0; --i)
{
AdjustDown(a, n, i);
}
int end = n - 1;
while (end >0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
--end;
}
}
两种除了建堆的方式不一样,后面的操作都是一样的,我们发现对于第一种,我们要谢两种算法,一个是向上调整算法和向下调整算法,而第二种只需要谢一个向下调整算法,有的人就说,那就第二种算法比较好,算法的好坏看效率,因为他们就建堆的时候不一样,所以我接下来将为大家证明向上和向下调整的时间复杂度
排升序:建大堆
排降序:建小堆
二、向下调整算法的时间复杂度
因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响最终结果):
采取的是错位相减法,和满二叉树的性质,讨论的都是最坏的情况,我们向下调整算法的时间复杂度为:o(n);
三、向上调整算法的时间复杂度
我们看到向上调整算法的时间复杂度是o(n*logn);
所以在堆排序算法中,只写一个向下调整算法即省事效率又高
四、总结
讲到这里我相信对于堆排序的算法已经非常了解了,这篇是建立在关于上一篇知识的基础上才能更好的去理解,对于两种方法实现,我已经将比较好的方法通过对比展现出来了,希望读者可以认真的学习到这篇的知识,接下来的一篇也是关于堆的应用,希望读者来支持博主一下