一、时间复杂度
冒泡排序是一种常见的简单排序算法,它的平均时间复杂度为O(n²)。假设从小到大对数组进行冒泡排序,那么最好情况下(待排序元素为顺序排列)的时间复杂度为O(n);最坏情况下(待排序元素为逆序排列)的时间复杂度为O(n²)。
二、算法原理
假设排序顺序为从小到大,冒泡排序算法每趟排序都会将待排元素中最大的元素像冒泡一样顶到数组末尾。经过 n 趟排序,数组就可由小至大排好顺序,其中 n 为数组中元素的个数。
一趟排序:
从上到下比较两个相邻的元素,如果顺序正确,则元素位置保持不变;如果是逆序,则交换两个元素的位置。完成一趟排序后,最大的元素处于数组最后的位置。
n 趟排序:
完成一次排序后,再对剩余元素重复一趟排序过程。一共重复上述排序过程 n 次。
三、算法优化
考虑这样一种情况:如果在中间某一步,数组已经完成排序,完全有序。那么还需要继续进行 n 趟排序 吗?
显然,不需要。可以针对这种情况,对算法进行优化。如果在中间某一步,数组已经完成排序,完全有序,那么直接退出剩下的排序。
那边如何判断数组是完全有序的呢?
在一趟排序中,没有任何一对元素需要交换位置,则说明完全有序。可以编写swap函数,如果从未调用swap函数,则说明数组已经完成排序。
四、代码实现
定义了冒泡排序函数 Bubbling_sort,传入参数为待排序的数组及数组元素个数。函数包含两层循环,内层循环完成一趟排序。遍历整个待排序数组,比较相邻两元素的大小,如果前一个元素大于后一个元素(逆序),则使用 swap 函数交换两元素位置。否则,则不改变元素位置。完成一趟排序后,待排序数组除去最后一个排好序的元素。
外层循环为 n 趟排序,此时待排序数组的范围为前 n - 1 个元素。当最后一个元素为原数组的第一个元素时,排序完成。
swap 函数中可以设置标志位,调用函数则标志位发生改变。Bubbling_sort 中添加标志位判断语句,与初始值不同,则说明此趟交换过元素,排序还未完成。
#include <iostream>
using namespace std;
//交换元素位置
void swap(int& a, int& b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
//打印数组
void printarr(int a[], int n)
{
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
cout << endl;
}
void Bubbling_sort(int a[], int n)
{
int num = 0;
for (int last_position = n - 1; last_position >= 0; last_position--)
{
int flag = 0;
//一趟冒泡
for (int j = 0; j < last_position; j++)
{
if (a[j] > a[j + 1])
{
swap(a[j], a[j + 1]);
flag = 1; //标识发生了交换
num++;
}
}
if (flag == 0) break; //全程无交换
printarr(a, n);
cout << "num = " << num << endl;
}
}
int main()
{
int a[] = { 34, 8, 64, 51, 32, 21 };
Bubbling_sort(a , 6);
return 0;
}
五、算法特点
首先介绍一个概念:稳定。
稳定:只有当前元素严格大于下一个元素才做交换,如果二者相等,不做交换。
显然,冒泡排序是一种稳定的排序算法。此外,它还有一个优点,就是普遍适用。假如所有待排元素都放在链表中,依旧可以使用冒泡排序算法。
冒泡排序的缺点就是算法时间复杂度较高,没有快速排序、堆排序快。
参考文献
浙江大学《数据结构》