堆排序是利用堆这种数据结构设计的一种排序算法,是排序算法中最复杂的一个。本文参考了https://www.cnblogs.com/chengxiao/p/6129630.html,自己对堆排序做一个记录和总结
不稳定排序,最差最好平均的时间复杂度都是O(nlogn)。空间复杂度是O(1)
堆有大顶堆和小顶堆,
大顶堆:每个结点的值都大于等于左右子结点的值称为大顶堆
小顶堆:每个结点的值都小于等于左右子结点的值称为小顶堆
对堆中的结点按层进行编号,映射到数组中如下:
对堆排序的很清晰易懂的演示:https://www.bilibili.com/video/av18980178/
流程就是:
先把输入的数组创建一个大顶堆,此时头结点是最大的
将头结点与尾节点交换,然后在树中删除尾节点(即删除了最大的数),此时最大的数排列到了数组的最后面
再对剩下的数创建一个大顶堆,交换头尾结点,并删除尾节点,依次类推,直到最后剩余一个数
这样不断地 创建大顶堆,交换头结点,删除最大数,最终得到一个从小到大的排序数组。注意删除并不是实际意义上的删除,只是在堆中排除这个数,在调整大顶堆的时候忽略这个数。
//堆化,即调整成最大堆
void heapify(int a[], int start, int end)
{
int dad = start;
int son = dad * 2 + 1; //dad的左子结点
while (son <= end) //结点下标在范围内
{
//比较左右节点,选择大的那个与父结点比较
if (son + 1 <= end && a[son] < a[son + 1]) //如果左子结点小于右子结点,son指向右子结点
son++;
if (a[dad] > a[son]) //如果父结点大于子结点,不需要调整,返回
return;
else //如果父结点小于子结点,交换父子结点,然后继续比较子孙结点
{
swap(a[dad], a[son]);
dad = son;
son = dad * 2 + 1;
}
}
}
//实现堆排序
void heapSort(int a[], int len)
{
//1、构建大顶堆
//从最后一个父结点开始调整成大顶堆,从下往上,从左往右
for (int i = len / 2 - 1; i >= 0; i--)
heapify(a, i, len - 1);
//2、交换顶结点与尾节点,再对剩下的数构建大顶堆
//循环利用i-- 把交换后的尾节点删除,实际上没删除!
for (int i = len - 1; i > 0; i--)
{
swap(a[0], a[i]); //交换顶结点与尾节点
heapify(a, 0, i - 1); //表示删除了尾节点,再对剩下的数调整成大顶堆
}
}