代码中所用到的结构体
typedef struct heaptype
{
int elem[MAX+1];//0位空着
int length;
}Heaptype;
堆排序的基本概念
- 堆排序属于选择排序:出发点是利用前一次比 较的结果,减少“比较”的次数
- 小根堆:1.对于任意一个非叶结点的关键字,都不大于其左、右儿子 结点的关键字。2.在堆中,以任意结点为根的子树仍然是堆。特别地,每个 叶结点也可视为堆。每个结点都代表(是)一个堆。3.在堆中(包括各子树对应的堆),其根结点的关键字是最 小的。去掉堆中编号最大的叶结点后,仍然是堆。
- 大根堆,定义类似于小根堆,这里不再重复。
- 这里要注意的是,虽然图示以树形的形式表示出来,但是在代码中依旧是数组的形式存储。在堆排序中,用到的是大根堆。
堆排序中的堆重建
- 在这里,是将输入该函数的堆视为只有根结点不一定满足大根堆的性质,其子树均满足。
- 故通过一个for循环,先利用点 i 的左儿子一定为 2i ,右儿子一定为 2i + 1 的性质,来比较根结点是否满足大根堆的性质,假设不满足,则跟左右儿子中较大的值互换。
- 互换之后该根节点的值满足了大根堆的性质,进而将当前根结点指针改为指向与当前根结点互换的点,继续判断是否满足大根堆的性质。
- 直到根结点指针指向的节点左右儿子不存在。
void HeapAdjust(Heaptype *H,int s,int m)//除了H->elem【s】外都是大根堆,进行调整,使其变为大根堆,s为开始调整根的位置,m为H的数目
{
int j;//j表示后来的下标
for(j = 2*s;j <= m;j *= 2)//for循环对从s位置为根的树进行重建最大堆,当无左子树时停止
{
if(j<m && H->elem[j] < H->elem[j+1])//判断是否有右子树,并判断左子树和右子树的大小
{
if(H->elem[s] < H->elem[j+1])
{
swap(&H->elem[s],&H->elem[j+1]);//右子树大则交换
s = j+1;//根位置移动到右子树
j = s;//j移动到根的位置
}
}
else if(H->elem[s] < H->elem[j])//判断右子树和根的大小
{
swap(&H->elem[s],&H->elem[j]);//左子树大则交换
s = j;//将根的下标移动至左子树
}
}
}
堆排序中的初建堆及其排序的完成
- 首先,通过完全二叉树在数组里的性质,找到最后一个不为叶子结点的结点,从该结点向根结点调用Heapadjust函数完成大根堆的初建
- 之后,从数组的最后一个元素所在位置开始,依次将大根堆的根放在数组的尾部,每放一次,则输入Heapadjust函数的尾部减一,并且进行一次大根堆的重建,数组的尾部进行到1为止。
void Heapsort(Heaptype *H)//初建堆
{
int i;
for(i = H->length/2;i>0;i--)//从最后一个不为叶子节点的结点开始调整
{
HeapAdjust(H,i,H->length);
}
for(i = H->length;i>1;i--)//开始出堆
{
swap(&H->elem[1],&H->elem[i]);//将大根堆的最大值排到序列的最后
HeapAdjust(H,1,i-1);//对交换后的数据排列
}
}
源代码
#include <stdio.h>
#include <stdlib.h>
#define MAX 10000
typedef struct heaptype
{
int elem[MAX+1];//0位空着
int length;
}Heaptype;
void swap(int *p1,int *p2)
{
int temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
}
void HeapAdjust(Heaptype *H,int s,int m)//除了H->elem【s】外都是大根堆,进行调整,使其变为大根堆,s为开始调整根的位置,m为H的数目
{
int j;//j表示后来的下标
for(j = 2*s;j <= m;j *= 2)//for循环对从s位置为根的树进行重建最大堆,当无左子树时停止
{
if(j<m && H->elem[j] < H->elem[j+1])//判断是否有右子树,并判断左子树和右子树的大小
{
if(H->elem[s] < H->elem[j+1])
{
swap(&H->elem[s],&H->elem[j+1]);//右子树大则交换
s = j+1;//根位置移动到右子树
j = s;//j移动到根的位置
}
}
else if(H->elem[s] < H->elem[j])//判断右子树和根的大小
{
swap(&H->elem[s],&H->elem[j]);//左子树大则交换
s = j;//将根的下标移动至左子树
}
}
}
void Heapsort(Heaptype *H)//初建堆
{
int i;
for(i = H->length/2;i>0;i--)//从最后一个不为叶子节点的结点开始调整
{
HeapAdjust(H,i,H->length);
}
for(i = H->length;i>1;i--)//开始出堆
{
swap(&H->elem[1],&H->elem[i]);//将大根堆的最大值排到序列的最后
HeapAdjust(H,1,i-1);//对交换后的数据排列
}
}
int main()
{
Heaptype *H;
H = (Heaptype*)malloc(sizeof(Heaptype));
int i;
printf("请输入要排序的个数:\n");
scanf("%d",&H->length);
getchar();
printf("请输入数据:\n");
for(i=1;i<=H->length;i++)
{
scanf("%d",&H->elem[i]);
getchar();
}
Heapsort(H);
printf("排序后:\n");
for(i=1;i<=H->length;i++)
{
printf("%d ",H->elem[i]);
}
}
测试样例
25
5 8 9 6 7 4 3 5 2 1 11 18 19 17 21 26 29 35 38 31 36 30 34 32 70
测试结果