一、前言
排序是程序开发中极为常见的操作,而冒泡排序是最为经典的基础排序算法。它的逻辑清晰易懂、代码实现简洁,因而成为每一位编程初学者的入门必修课。本文会讲清楚冒泡排序的逻辑和实现方法。
二、理解冒泡排序
冒泡排序的核心思想是:两两相邻元素进行比较。我们以下面一个数组为例:
int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
假设我们想要将数组中的元素按升序排列。所谓“升序”,无非就是“把大的元素放在后面”。那么我们先从第1、2个元素开始判断。由于这两个元素10、9不符合“先小后大”的顺序,我们对这两个元素进行交换,交换后的数组内容变成了:
9,10,8,7,6,5,4,3,2,1
接下来我们再把第2、3个元素进行比较。仍然不符合升序,那么进行交换:
9,8,10,7,6,5,4,3,2,1
以此类推。当我们进行了9次比较与交换后,数组内容就变成了:
9,8,7,6,5,4,3,2,1,10
最大的数字10被交换到了最后,待在了它应该在的位置上。
以上的过程,就称为“一趟排序”。
我们再进行一趟排序,会发现第二大的数字9也到了它应该在的位置上:
8,7,6,5,4,3,2,1,9,10
由此可见,在冒泡排序算法中,一趟排序可以将1个数字放在正确的位置上,而且总是先解决位置在最后的数字,再依次向前解决剩下的数字。
此外,在第二趟排序中,我们发现:由于最大的数字10已经在正确的位置上,我们其实可以省略最后一次比较和交换,只进行8次。那么以此类推,每进行一趟排序,一趟中所需要的比较和交换次数就少一次。
那么排序一组数据一共需要多少趟呢?
假设我们需要排序n个元素。由于一趟排序可以解决1个元素,那么进行(n-1)趟排序之后,剩下的最后一个元素肯定也在正确的位置上了。所以我们一共只需进行(n-1)趟排序。
再回顾以上的过程,我们发现在每一趟排序里,都是最大的数字从杂乱的数据中一点一点移动到最后,正如水中的气泡浮到水面的过程。这就是“冒泡排序”得名之由。
三、冒泡排序算法的代码实现
3.1 实现
我们将冒泡排序的过程封装成一个函数,那么这个函数需要两个参数:待排序的数组arr和需要进行排序的元素个数sz。首先,我们需要一个运行(sz - 1)次的循环,代表一共进行(sz - 1)趟排序。
void bubble_sort(int arr[], int sz)
{
int i;
for (i = 0; i < sz - 1; i++)
{
}
}
由于每进行一趟排序,一趟排序内部需要进行的比较和交换次数就少一次,所以每趟内部需要一个运行(sz - 1 - i)次的循环,代表相邻元素比较和交换的次数。
void bubble_sort(int arr[], int sz)
{
int i;
for (i = 0; i < sz - 1; i++)
{
int j;
for (j = 0; j < sz - 1 - i; j++)
{
}
}
}
最后我们写出比较和交换相邻元素的过程。
void bubble_sort(int arr[], int sz)
{
int i;
for (i = 0; i < sz - 1; i++)
{
int j;
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;
}
}
}
}
现在我们就完成了冒泡排序的全过程。我们创建一组实验数据进行测试:
int main()
{
int arr[10] = { 3,5,4,7,9,10,1,8,2,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
int i;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
运行结果如下:

成功完成了排序。
3.2 优化
以上代码确实可以完成排序,但是,如果还没有进行完(sz - 1)趟排序,数据就已经完成排序了,后面的排序趟数就多余了。
为了优化代码,我们可以设置一个整型变量flag,flag为1时代表数据已经有序,不需要再排列,flag值为0时代表还需要继续排列。
在每一趟中我们先将flag值设置为1,也就是假设数据已经有序。
在相邻元素比较、交换的if语句中,加上一句flag = 0,如果这个语句被执行,就说明发生了交换,也就是数据仍没有完全排列好。
如果一趟排序完成后flag值仍为1,说明这一趟没有进行交换,也就说明数据已经排列完成了。此时我们就用break语句来结束循环。
void bubble_sort(int arr[], int sz)
{
int i;
for (i = 0; i < sz - 1; i++)
{
int flag = 1;//假设这一趟已经有序
int j;
for (j = 0; j < sz - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
flag = 0;//发生交换,说明没有排列完成
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
if (flag == 1)//没发生交换,已经排列好了,不需要再进行后续排序
break;
}
}
优化后的代码只会进行1趟多余的排序(需要一趟排序来判断数据是否已经有序)。
以上就是文章的全部内容。
1万+

被折叠的 条评论
为什么被折叠?



