实验课7-15堆排序

本题目要求读入N个整数,采用堆排序法进行排序,输出前3轮排序后的结果。

输入格式:

输入不超过100的正整数N和N个整数(空格分隔)。

输出格式:

输出三行,第一行为第一轮排序结果,第二行为第二轮排序结果,第三行为第三轮排序结果。数据间用一个空格分隔。

为简便起见,最后一个元素后也有一个空格。

输入样例:

5
3 1 2 5 4

输出样例:

3 4 2 1 5 
1 3 2 4 5 
2 1 3 4 5 

Ciallo~(∠・ω< )⌒★

思路: 

这道题是最近学的排序方法中的一个,个人非常喜欢堆排序,因为在几个基本排序算法里堆排序算是比较快的,而且应用到了前些时候学到的树的思想。

首先,堆排序需要先将给出的数据按顺序构成一棵完全二叉树,按题目里给出的输入样例的话,就是像这样构造一个:

 然后接下来是第一步

堆的初始化:

从定义上说,我们要构造一个大根堆,顾名思义,大根堆就是根结点的数值比较大,接着在各子树上都是父节点比子节点要大,这样依次传递。构造的过程需要先从完全二叉树最后一个非叶节点开始找起,依次比较它与它的子节点的大小,然后将两个子节点中最大的数值上浮到父节点的位置。简单来说,就是把这个树里面沉在底部的大结点都上浮到上层,就像筛沙子一样,根据上面已经构造好的树来说,我们的初始堆化的过程如下:

到此,初始堆化完毕,值得一提的是,初始堆化并不能算作堆排序的排序过程,它只是首先构建了一个堆,后面的步骤才能算作是堆排序,所以我们不能将刚刚得到的5 4 2 1 3输出。 

当然,初始堆化的过程我们可以写一个函数用来实现这个功能:

//函数中的a[]是存放需要排序数据的数组,i是根数据所在的数组下标,j是数组下标的上届
void heapify(int a[],int i,int j)
{
    int k,x;
    k=2*i;
    x=a[i];
    while(k<=j)
    {
        if(k<j)
            if(a[k]<a[k+1])
                k=k+1;
        if(x>=a[k])
            break;
        else
        {
            a[i]=a[k];
            i=k;
            k=2*i;
        }
    }
    a[i]=x;
}

接下来就是堆排序的过程:

堆排序:

堆排序的过程是将我们得到的大根堆变成数据从小到大排列的过程,这个过程里面,怎么将本来在上面的最大数值移到最后呢?很简单,只要将根从数组最开始和数组最末尾的数据交换即可,然后我们重新堆化除刚刚移过去的根以外的所有数据,这样就又能得到一个除去了刚刚最大数值的一个大根堆(也可以说,我们缩小了堆的范围)。这样,就算做堆排序的一轮排序完成,接下来我们只要重复有限次以上步骤,就可以完成对数据的排序。

简单的堆排序过程如下,我省略了重新堆化的详细步骤,和上面的过程并无二异:

 我们可以看到,第一轮,第二轮,第三轮排序结果,与我们的输出样例完全相同,那么,这个过程就是正确的,接下来是要怎么用代码来实现它:

//数组a[]存储原始数据,n是数组下标上届(数据个数)
void heap_sort(int a[],int n)
{
    int i,x,k=0;
    for(i=n/2;i>=1;i--)
        heapify(a,i,n);//初始堆化,循环是需要进行多次下沉操作(一个节点最多下沉n/2次)
    for(i=n;i>1;i--)
    {
        x=a[1];
        a[1]=a[i];
        a[i]=x;
        
        for(int I=1;I<=n;I++)
        {
            printf("%d ",a[I]);
        }
        printf("\n");//输出每一轮排序后的序列
        
        heapify(a,1,i-1);//重新堆化
    }
}

那么,这个代码已经非常简单的呈现在我们面前了,接下来只需要根据题目要求做出一点细节修改就可以了。

代码:

#include<stdio.h>
#define N 100//题目要求不超过100个数
void heapify(int a[],int i,int j)
{
    int k,x;
    k=2*i;
    x=a[i];
    while(k<=j)
    {
        if(k<j)
            if(a[k]<a[k+1])
                k=k+1;
        if(x>=a[k])
            break;
        else
        {
            a[i]=a[k];
            i=k;
            k=2*i;
        }
    }
    a[i]=x;
}

void heap_sort(int a[],int n)
{
    int i,x,k=0;
    for(i=n/2;i>=1;i--)
        heapify(a,i,n);
    for(i=n;i>1;i--)
    {
        x=a[1];
        a[1]=a[i];
        a[i]=x;
        if(k<3)//题目要求前三次排序输出
        {
            for(int I=1;I<=n;I++)
            {
                printf("%d ",a[I]);
            }
            printf("\n");
            k++;//k计数器
        }
        heapify(a,1,i-1);
    }
}

int main()
{
    int a[N],n,i;
    scanf("%d",&n);
    for(i=1;i<=n;i++)//注意,因为我们构造的是树,有父节点有子节点,不能用数组第一位a[0]存放数据
    {
        scanf("%d",&a[i]);
    }
    heap_sort(a,n);
}

看的出来,代码确实很简单。那么,各位小土豆们应该会有更好的思路,可以互相学习一下。

でわ、終わります。 

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值