数据结构(八)——内部排序之堆排序

本文介绍了堆排序的基本概念,包括小根堆和大根堆的定义,并详细阐述了堆排序中的堆重建过程以及如何初建堆并完成排序。堆排序是一种选择排序,利用大根堆性质减少比较次数。在代码实现中,堆是用数组形式存储的。文章还提供了完整的源代码和测试样例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代码中所用到的结构体

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
测试结果
结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值