排序之冒泡排序

        上一节中的例子中董老师想为他教的五名优秀学生进行成绩排名,我们运用桶排序的算法很快地帮董老师解决了这个问题。现在,董老师提出来新的要求,那就是他想找出成绩最好的那名同学去参加比赛。我们已知这五名同学的名字分别为敏敏(95分)、哲哲(93分)、锐锐(95分)、帅帅(92分)、柔柔(98分),想一想,如果我们运用上一节的内容能否满足董老师的要求呢?

        通过观察与思考,我们发现上一节所介绍的简化版的桶排序算法只能对成绩进行排序,最终输出的也仅仅是分数序列,并没有对人本身进行排序。也就是说,我们现在并不知道排序后的最高分原本对应着哪一名同学!怎样才能帮助到董老师呢?这就要涉及到本节要介绍的内容了——冒泡排序。

        冒泡排序是基于交换的排序,它的基本思想是:每次比较两个相邻的元素,如果他们的顺序错误就把他们交换过来。冒泡排序相较于桶排序更节省空间,它不仅能对整数进行排序也能对小数进行排序。

        下面我们以 21 53 99 81 67 这5个数的降序(从大到小)排列为例,介绍一下冒泡排序的实现过程。

        首先我们比较第1位和第2位的大小,现在第1位是21,第2位是53。很显然,21比53要小。我们希望数字按照降序进行排列,也就是说越小的数字应该越靠后,因此需要交换这两个数的顺序。交换后这5个数的顺序为 53 21 99 81 67。

        按照刚才的方法,继续比较第2位和第3位的大小,第2位是21,第三位是99.21比99要小,因此需要交换两个数的位置。交换后的顺序为 53 99 21 81 67。

        根据以上规则,继续比较第3位和第4位的大小,如果第3位比第4位小,则交换两个数的位置。交换后的顺序为 53 99 81 21 67。

        最后,比较第4位和第5位的大小。经过4次比较后原数列变为 53 99 81 67 21。

        我们发现4次比较后最小的一个数已经到了最后一位,回忆一下刚才比较的过程:每次都是比较相邻的两个数,如果后面的数比前面的数大,则交换这两个数的的位置。一直比较下去直到最后两个数比较完毕后,最小的数就移动到了最后一个。就如同气泡一样,一步一步往后“翻滚”,直到最后一位。所以这个排序的方法被形象的称为“冒泡排序”。

        仔细观察,排序进行到现在我们只讲5个数中最小的一个数移动到了相应的位置。,即只有最小的一个数归位了。每将一个数归位我们将其称为“一趟”。下面我们重复刚才的过程,将剩下的4个数逐一归位。

        “第二趟”我们的目标是将第2小的数归位。首先还是比较第1位和第2位,如果第1位比第2位小,则交换这两个数的位置。交换后这5个数的顺序是 99 53 81 67 21。接下来的过程同“第一趟”一样,依次比较第2位和第3位,第3位和第4位即可。注意现在我们已经不必比较第4位和第5位了,因为经过“第一趟”的比较与交换已经确定了第5位上放的是最小的数了。第二趟结束后的顺序为 99 81 67 53 21.

        “第三趟”、“第四趟”也是一样的。这里有同学就要问了,这不是已经排好了吗?还要继续吗?当然,这个例子纯属巧合,再换一组例子可能就不是了。想一想,什么样的数据样例需要到最后一趟才能排好序。

        从上述过程中我们不难看出“冒泡排序”的原理:每一趟只能确定将一个数归位。总结一下就是:如果有n个数进行排序,只需将n-1个数归位,也就是说要进行n-1趟操作。而每一趟都需要从第1位开始进行相邻两个数的比较,将较小的数放在后面,比较交换完毕后继续比较下面两个相邻数的大小,重复此步骤,直到最后一个数归位。

        呼......絮絮叨叨了半天,也不知道有没有介绍清楚,还是直接看代码吧。

#include <stdio.h>
​
int main()
{
    int a[100], i, j, t, n;
    scanf("%d", &n); //输入一个数n,表示接下来有n个数
    for(i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    
    //冒泡排序的核心代码
    for(i = 1; i <= n-1; i++) //n个数排序,只用进行n-1趟 
    {
        //从第1位开始比较直到最后一个尚未归位的数 
        for(j = 1; j <= n-i; j++)
        {
            //比较大小并交换 
            if(a[j] > a[j+1]) //升序 
            //if(a[j] < a[j+1]) //降序 
            {
                t = a[j];
                a[j] = a[j+1];
                a[j+1] = t;
            }
        }
    }
    
    //输出结果 
    for(i = 1; i <= n; i++)
        printf("%d", a[i]);
    
    getchar();getchar(); //同system("pause"); 
    
    return 0;
} 

输入样例:

10 
8 99 50 22 55 1 6 9999 1000 0

输出样例:

0 1 6 8 22 50 55 99 1000 9999

        现在我们可以用程序满足董老师的需求了,只需要将上面的代码稍加修改就好了。

#include <stdio.h>
​
struct student
{
    char name[21];
    int score;
}; //这里创建了一个结构体用来存储姓名和分数 
​
int main()
{
    struct student a[100], t;
    int i, j, n;
    scanf("%d", &n); //输入一个数n
    for(i = 1; i <= n; i++) //循环读入n个人名和分数  
        scanf("%s %d", a[i].name, &a[i].score);
    
    //按分数从高到低 进行排序 
    for(i = 1; i <= n-1; i++) 
    {
        for(j = 1; j <= n-i; j++)
        {
            if(a[j].score < a[j+1].score)
            {
                t = a[j];
                a[j] = a[j+1];
                a[j+1] = t;
            }
        }
    }
     
    printf("%s", a[1]); //输出人名 
    
    getchar();getchar();
    
    return 0;
} 

输入数据:

5
minmin 95
zhezhe 93
ruirui 95
shuaishuai 92
rourou 98

运行结果:

rourou

        冒泡排序的核心部分是双重嵌套循环。不难看出冒泡排序的复杂度是O(N²),这是一个非常高的时间复杂度。那还有没有更好的排序算法呢?当然,我们下节继续——快速排序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋风落叶总萧瑟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值