嗨嗨~大家好呀,关于初始C语言这一个系列,我打算每周二给大家更新(也有可能不定期加更哦)。主要是身为大一学生总是忙碌的,再加上马上又要考四级等一系列其他的事情,我写博客的时间其实是很少的。更新频率不高还望多多谅解(说不定我这系列更新频率都没你们学的快:( )好了废话少说,让我们开始今天的主题————冒泡排序。
首先,要学习冒泡排序,你得先对嵌套循环有一定的了解,如果你还对嵌套循环有些许陌生的话,建议可以看我上一期的初始C语言,我对嵌套循环做了一定的介绍,希望能对你有所帮助。
如果你已经有所了解,那我们就正式开始介绍冒泡排序。首先,我们先了解————
一.什么是冒泡排序
排序的意思很好理解,就是随便给你一组数据,要求你对这组数据进行从大到小或是从小到大进行排序。而对于冒泡的含义,我总结而言就是从这组数据的第一个开始根据下标依次往上冒,按照你需要的要求(从小到大或是从大到小等)确定该数据与和它相邻数据是否进行交换。
光这么说可能很抽象,那么下面我将详细给大家介绍:
二.冒泡排序的实现原理
假设我给出了如下的一组数据,现在要求你对他们进行升序排列(假设下标从0开始):
23 54 11 98 32
那我们要怎么操作呢?首先,根据我总结出的冒泡含义,我们就从第一个数字(下标为0)23开始:
我那么根据冒泡的含义,我们现在进行要求判断,由于题目要求我们的是升序排序,所以我们将23和54进行比较,发现23比54还要小,符合题意,那我们就不进行交换。
既然是根据下标依次往上冒,那么在两者进行完操作后,现在我们将目光定在54这个数字(下标为1)上,让他和下一个数字11进行比较,不难发现,54比11要大,显然是不符合题目要求的,所以在这时我们就要开始进行交换操作。
进行完操作后,继续对下一个下标进行操作,下一个下标对应的数仍然是54(下标为2),那么我们发现54比98要小,所以不进行交换操作。
最后,我们对数字98操作(下标为3),发现98比32要大,不符合题意,所以需要进行交换操作:
至此,第一轮冒泡排序就此结束 。所以我们可以总结出在这一轮冒泡排序结束的条件,那就是所有数据都进行了比较操作,并且确定出了最后一个可以不需要再进行比较的数。没错,在这一轮冒泡排序中,我们会发现98是这组数据中最大的数,也就是说,我们在进行下一轮的冒泡排序时,就可以不用和最后一个数据进行比较,因为这个位置它已经坐实了,就是最大的,谁也抢不走。同理,如果我们要求的降序排序,那么最后一个数字就是这组数据中最小的数字。
那么,冒泡排序的原理就是如此,既然说了这是第一轮冒泡排序,那也就说明冒泡排序并没有就此结束,我们观察这些数据也能发现,这些数据并没有按照题目要求降序排序,仅仅只是确定出了这组数据里的最大数在最后。那么,问题来了:
①我们什么时候才能结束冒泡排序呢?
②下一轮的冒泡排序和上一轮有不同吗?如果有,该怎么实现呢?
③我们该如何写代码,具体实现该冒泡排序呢?
这些问题,我将在下一点中着重讲解:
三. 冒泡排序的具体实现和代码讲解
①我们什么时候才能结束冒泡排序呢?
在上一节里我们就已经讲过,第一轮的冒泡排序里已经把最后一个数据确定了下来。那就说明,我们在下一轮冒泡排序中就可以不需要再拿最后一个数据进行比较。那么,在第二次冒泡排序结束后,我们就同样可以确定这组数据中的倒数第二个数据,因为最后一个不会进行比较,该组数据中的倒数第二个数据就是该轮冒泡排序中的最后一个数据。由此就可以知道,本体只给出了5个数据,也就是说我们只要进行4轮冒泡排序(确定完该组数据的第二个数据后第一个也就确定好了)就可以完全确定所有下标所对应的正确的数字,即排序成功。
示例图(红色即为确定好的不再需要更改的数据)
那我们就可以拓展思维,如果随机给你了N个数据,那么你需要进行几轮冒泡排序呢?看到这,想必答案就已经出来了,那就是N - 1次。
②下一轮的冒泡排序和上一轮有不同吗?如果有,该怎么实现呢?
这个答案就显而易见了,明显上一轮的冒泡排序参与比较的数据要比下一轮冒泡排序参与比较的数据要多一个。所以这是不同的。同样我们可以看到,在第一轮循环中,我们一共需要进行4次比较,而在第二轮循环中,我们一共需要进行3次比较......以此类推,我们在下一轮冒泡排序时进行比较的次数永远是比上一次的次数少一次的。也就是说,每一轮冒泡排序中所需要参与比较的次数也是不同的。理清楚了这些,我们就可以着重开始想如何实现了。相信很多人看到开头我多次强调嵌套循环的时候就猜到了,没错,这里我们就需要使用嵌套循环去实现每一轮的冒泡排序以及每一轮冒泡排序里所参与的数据比较。
③我们该如何写代码,具体实现该冒泡排序呢?
下面给大家展示本道题的冒泡排序实现的代码:
#include <stdio.h>
int main()
{
int i = 0, j = 0; //循环变量
int nums[5] = {23,54,11,98,32}; //需要进行排序的数据
int temp = 0; //中间变量,临时存储变量
for (i = 0; i < 4; i++) //外层循环,控制的是第几轮冒泡排序
{
//内层循环,控制的每一轮冒泡排序中需要进行数据比较的次数
for (j = 0; j < 4 - i; j++)
{
//如果正在进行比较的数据比紧挨着它的下一个数据大就进行交换,否则不做任何动作
if (nums[j] > nums[j + 1])
{
temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
//打印结果
for (i = 0; i < 5; i++)
{
printf("%d\t",nums[i]);
}
return 0;
}
排序完之后的数据(使用的是VS2022编译器):
在这里内层循环的跳出条件j < 4 - i中的4 - i含义就是实际在该轮冒泡排序中实际需要进行比较的次数,而这个数字正好可以通过所需要进行冒泡排序的轮数减去所在的冒泡排序轮数。由此可以推出通用情况,假如我们有N个数据需要进行冒泡排序来进行排序,那么我们就需要进行N - 1次冒泡排序,这同样是外层循环跳出条件的取不到的极限值(取不到是因为循环是从0开始),每轮冒泡排序就要进行N - i - 1次比较,这同样是内层循环跳出条件的取不到的极值(取不到同样是因为循环从0开始)。就此,冒泡排序全部完成。
四.总结
介绍完了冒泡排序,很多人也许会问,冒泡排序在我们实际使用时很常用吗?我的回答是,在处理数量较少的数据时冒泡排序很管用,但是一旦数据数量多了起来,冒泡排序就不再那么好用了。并不是因为冒泡排序不会实现排序功能,而是因为它实在是太慢了。如果你学过数据结构,知道如何算时间复杂度,你就会知道,冒泡排序的时间复杂度是O(n^2)。假如给你1000个数据要你进行排序,每执行一次循环就要花费1毫秒的时间,那么冒泡排序的实现就需要1000^2毫秒才能实现,这就不难发现冒泡排序的弊端是处理过多数据时需要花费的时间长。而在我们参加竞赛时,每一道题目对于时间的要求还是很严格的,所以在这时如果需要对数据进行排序的话,使用冒泡排序很有可能就会造成运行超时的情况。因此,冒泡排序对我们而言其实并不是那么的泛用。
好了,今天的冒泡排序就介绍这里,觉得对你有帮助的话还请给我点一个赞,加个关注,谢谢大家支持,如果又哪里不懂或是文章出现问题欢迎大家评论区指出,那我们就下星期二再见啦~