文章目录
前言
堆排序(Heap Sort)就是对直接选择排序的一种改进。此话怎讲呢?直接选择排序在待排序的n个数中进行n-1次比较选出最大或者最小的,但是在选出最大或者最小的数后,并没有对原来的序列进行改变,这使得下一次选数时还需要对全部数据进行比较,效率大大降低。
堆排序算法是Floyd和Williams在1964年共同发明的,同时他们发明了“堆”这种数据结构。
————————————————
一、堆排序的原理以及基础知识
1.大顶堆&&小顶堆
- 堆是具有下列性质的完全二叉数:每个节点的值都大于或等于其左右孩子节点的值,称为大顶堆(大堆);或者每个节点的值都小于或者等于其左右孩子节点的值,称为小顶堆(小堆)
大小顶堆如图所示:
2.堆的物理结构与逻辑结构
- 堆的逻辑结构是一颗完全二叉数
- 堆的物理结构是一个数组
- 通过下标父子节点的关系:
leftchild=parent2+1
rightchild = parent2+2
parent = (child-1)/2(child是左孩子或者右孩子) - 堆的两个特性:
(1).结构性:用数组表示的完全二叉数
(2).有序性:任一节点的关键字是其子树所有节点的最大值(或者最小值)
图形:
二、推排序的步骤
1.建堆
(1)向下调整算法
- 前提: 左右子树均为大堆。
- 操作:从根节点开始,选出左右孩子小的那一个,跟父亲比较,如果比父亲小就交换,然后再继续往下调,调到叶子节点就终止
- 图文解释:
代码实现:(此情况只给出算法函数,具体调用到主函数中,最后会给出完整的堆排序程序代码)
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void AdjustDown(int* a, int n, int root)//向下调整算法
//时间复杂度:O(logN)
{
int parent = root;
int child = parent * 2 + 1;(左孩子公式)
while (child < n)
{
if (a[child + 1] > a[child] && child + 1 < n) {
++child;
//让child始终为左右孩子中小的那个
}
//小的往上浮,大的往下沉
if (a[child] > a[parent]) {
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else {
break;
}
}
}
(2)重复利用向下调整算法来建立大堆
- 这里我们采用自底向上的建堆方式:该建堆方式是从倒数第二层的节点(叶子节点的上一层开始),从右向左,从下向上的向下进行调整
- 最后一个非叶子节点的公式:(n-1-1)/2{利用此公式来计算的parent = (child-1)/2(child是左孩子或者右孩子)}
- 代码实现:(此情况只给出算法函数,具体调用到主函数中,最后会给出完整的堆排序程序代码)
for (int i =(n-1-1)/2 ;i >= 0;--i) {
AdjustDown(a, n, i);
}
2.排升序(以此为例子)
- 操作:第一个数据和最后一个数据交换,把他不看做堆里面的,前n-1个数据再进行向下调整算法,选出次大的数据,再进行交换,重复以往。
- 代码实现(此情况只给出算法函数,具体调用到主函数中,最后会给出完整的堆排序程序代码)
int end = n - 1;
while (end>0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
--end;
}
三、堆排序的时间复杂度计算
综上所述:总的时间复杂度为O(N*log(N))
总的代码实现:
#include<stdio.h>
void Print(int* a, int n) {
for (int i = 0; i < n; i++)
{
printf("%d ", a[i]);
}
printf("\n");
}
void Swap(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void AdjustDown(int* a, int n, int root)//向下调整算法,建堆
//时间复杂度:O(logN)
{
int parent = root;
int child = parent * 2 + 1;//(左孩子的公式)
while (child < n)
{
if (a[child + 1] > a[child] && child + 1 < n) {
++child;
//让child始终为左右孩子中小的那个
}
//小的往上浮,大的往下沉
if (a[child] > a[parent]) {
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else {
break;
}
}
}
void HeapSort(int* a, int n)
{
for (int i =(n-1-1)/2 ;i >= 0;--i) {//从倒数第一个非叶子节点开始调整
AdjustDown(a, n, i);
}
//排升序,要建大堆!!操作:第一个和最后一个交换,把他不看做堆里面。前n-1个数向下调整,选出次大的数,再跟倒数第二个位置交换
int end = n - 1;
while (end>0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
--end;
}
}
int main()
{
int a[] = { 3, 5, 2, 7, 8, 6, 1, 9, 4, 0 };
HeapSort(a, sizeof(a) / sizeof(int));
Print(a, sizeof(a) / sizeof(int));
}
总结
本文详细介绍了选择排序的优化算法–堆排序,大大降低了时间复杂度,提高了排序的效率。