1.文字语言描述
冒泡排序(BubbleSort) 是排序算法中最简单的之一,是一种简单直观的排序算法,这种算法就像是鱼在水中吐泡泡一样,一次吐一个泡,一个泡接一个泡的往上漂,冒泡排序算法也是一样,一次排出一个元素,经过多次的排序,一个接一个的元素被”吐“出来,最终完成整个序列的排序。
实现思路:通过循环重复遍历待排序的序列,每一次遍历都从头开始依次比较相邻的两个元素,如果两个元素之间不满足想要的顺序,就进行一次交换,直至遍历到最后的时候,就算完成一次遍历,就会排序出一个符合要求的元素放在最后的位置,然后将此元素排出此序列,下一次遍历的时候不用再将此元素进行重复遍历判断即可。这样重复多次的遍历,直至全部元素按照要求的顺序排列,并且全部遍历结束,表示序列已经排列完成。
实现步骤:
- 第一步:从头开始遍历,依次比较前后两个相邻的元素,不符合要求就进行交换,直至遍历到倒数第二个元素,然后会与最后一个元素进行比较,这样完成一次遍历,就会排出一个符合要求的元素,放在了最后一个元素的位置,然后将此元素在下一次遍历中排除(即下一次遍历少遍历一个元素,就是最后的一个元素)。
- 第二步:重复第一步,从头开始遍历上一次排除元素后的剩余的元素序列,这样每一次都排出一个元素放在最后,直至排出(总元素个数-1)个元素的时候,第一个元素就一定是符合要求的元素,排序结束。
2.代码实现(重点)
以整型数组为例,将数组元素按照升序进行排列:
#include <stdio.h>
void Bubble_sort(int arr[], int sz)
{
int i = 0;
int j = 0;
for(i = 0; i < sz-1; i++)
{
for(j = 0; j < sz-1-i; j++)
{
if(arr[j] > arr[j+1])
{
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
void print(int arr[], int sz)
{
int i = 0;
for(i; i < sz; i++)
{
printf("%2d", arr[i]);
}
}
int main()
{
int arr[] = { 2, 43, 53, 23, 57, 45, 90, 67, 46, 99 };
int sz = sizeof(arr) / sizeof(arr[0]);
//打印排序前
print(arr, sz);
Bubble_sort(arr, sz);
//打印排序后
print(arr, sz);
return 0;
}
运行结果:
关键代码解释
- 第一层循环判断条件:
i < sz - 1
;外层循环中的i
表示每一次遍历排出来的元素个数,最多只需要排出总数-1
个元素就能完成排序。 - 第二层循环判断条件:
j < sz - 1 - i
;//内层循环判断每一次遍历,从头开始判断相邻的两个元素是否需要交换,sz - 1 - i
整体表示j
每次遍历只需要增加到倒数第二个元素,那么j+1
自然就是最后一个元素,然后进行两两比较即可;为什么要减去i
呢?因为i
表明每一次遍历排序排出的元素个数,并且这个元素是放在了最后一个元素位置上;所以排出一个元素,则在下一次的遍历中就不需要再进行比较所以j
的判断条件每次的变化是随着i
的变化而变化的
对于上述的代码实现,我们可以发现一个问题:如果序列本来就是有序的,或者说序列的无序程度很低,只需要遍历一两次就完成了排序,但是上面的代码实现中,即使已经完成了排序,但是程序仍然需要一次又一次的遍历,即使序列的顺序是正确的,没有元素交换,也会重复的循环遍历,直至全部遍历一遍,这样就相当于做了很多的无用功。
那么我们就可以考虑优化上述代码,让程序在一次遍历后都先判断这个序列是否在遍历的过程中产生了元素的交换,如果产生了元素的交换,说明这个序列还不是完全有序的,还需要进行排序;如果没有元素进行交换,则表明这个序列已经是有序的,不需要再继续进行遍历排序操作,则可以直接结束排序,提高排序的效率。
3.优化
优化方法:设置一个标志,在每一次遍历的过程中,如果产生了元素的交换,则改变这个标志,表示本次遍历有元素被交换,序列还不是完全有序的,还需要再进行一次遍历才能再判断序列是否已经排序完成;如果没有元素的交换,则不会进行标志的改变,表明序列已经排序完成,序列是完全有序的,那么在这一次遍历之后。所以在每一次的遍历后通过一个if判断
,如果标志没有改变,表明序列有序,排序完成,直接退出排序即可。反之,序列无序,还需继续遍历排序,再进行判断。
#include <stdio.h>
//冒泡排序
void Bubble_sort(int arr[], int sz)
{
int i = 0;
int j = 0;
for(i = 0; i < sz-1; i++)
{
int flag = 1; //标记
for(j = 0; j < sz-1-i; j++)
{
if(arr[j] > arr[j+1])
{
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
flag = 0; //如果有元素需要交换,那么就需要将标志改变
}
}
//每一次遍历结束后判断标记是否改变,如果标记没有改变,表明已经有序,直接break结束排序即可
if(1 == flag)
{
break;
}
}
}
//打印函数
void print(int arr[], int sz)
{
int i = 0;
for(i; i < sz; i++)
{
printf("%3d", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7 ,8 ,9 };
int sz = sizeof(arr) / sizeof(arr[0]);
//打印排序前
print(arr, sz);
Bubble_sort(arr, sz);
//打印排序后
print(arr, sz);
return 0;
}
4.总结
冒泡排序作为最简单的排序算法之一,相信通过以上的讲解和示例,还是很容易理解的,但是对于一些初学者来说,还是会有很多的陷阱在里面的,如上述代码中的循环判断条件为什么要这么写,可不可以改写呢,需要注意哪些东西呢?还希望读者可以多多动脑思考,以及多多动手实践,并尝试改变一些代码,观察程序运行结果会有什么不同,这样不仅能加强理解,同时还能加深印象。
那么到此为止,关于冒泡排序的分享就结束啦,如果有疑问或者发现文章的问题,欢迎评论区留言交流,我们下期再见啦!