学会了堆的一些基本功能,让我们了解一下用堆的思想来对数据进行排序的方法——堆排序
梦想成真之前,看上去总是那么遥不可及!
建堆
对数组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)忽略已经调整过的元素,将堆顶元素与最后一个元素进行交换
(2)向下调整调整为堆
(1)(2)反复进行

堆在结构上是一个二叉树,而逻辑上就是一个数组,所以排序之后,下标从1到N-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;
}
}
好了,这就是堆排序的内容,有问题的地方欢迎评论区留言,如果感觉有帮助,还请三连支持!!!