冒泡排序(Bubble Sort)是一种交换排序
冒泡排序的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
可以把冒泡排序理解为一种改进的交换排序算法。
那么,就先从交换排序算法说起吧。
其实,谈到排序算法,我们脑海中自然而然最容易想到的算法应该就是交换排序算法了。也就是利用两层循环内层循环探测到的元素与外层那个元素逐一比较,如果小就进行交换,这样每一趟就能找出一个最小的排在上面,如果以10个元素比较为例,则需要比较9+8+7+6+5+4+3+2+1=45次,下面看一下用C++实现的源代码:
#include <iostream>
using namespace std;
void BubbleSort(int k[], int n)
{
int i, j, temp, count1 = 0, count2 = 0; //count1记录比较次数,count2记录移动次数
for (i = 0; i < n - 1; i++)
{
for (j = i + 1; j < n; j++)
{
count1++;
if (k[i] > k[j])
{
count2++;
temp = k[j];
k[j] = k[i];
k[i] = temp;
}
}
}
cout << "总共进行了" << count1 << "次比较,进行了" << count2 << "次移动!" << endl;
}
int main()
{
int i;
int a[10] = { 1, 0, 2, 3, 4, 5, 6, 7, 8, 9 };
//int a[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
cout << "排序前的数组是:";
for (i = 0; i < 10; i++)
{
cout << a[i];
}
cout << endl;
BubbleSort(a, 10);
cout << "排序后的结果是:";
for (i = 0; i < 10; i++)
{
cout << a[i];
}
cout << endl;
return 0;
}
虽然交换排序完成了对数组的排序,但却不是最好的方法,它严格意义上不属于冒泡排序算法,因为它不满足上面提到的冒泡排序算法中比较相邻元素的要求。
正宗的冒泡排序:在上面交换排序的基础上,让i标记比较趟数,j从后往前循环(注意从后往前),从最后一个元素开始,如果比前一个小,就进行交换,代码作少量修改如下:
#include <iostream>
using namespace std;
void BubbleSort(int k[], int n)
{
int i, j, temp, count1 = 0, count2 = 0;
for (i = 0; i < n - 1; i++) //此处做了修改
{
for (j = n - 1; j > i; j--)//注意j是从后往前循环,注意这里的n代表数组长度
{
count1++;
if (k[j - 1] > k[j])
{
count2++;
temp = k[j - 1];
k[j - 1] = k[j];
k[j] = temp;
}
}
}
cout << "总共进行了" << count1 << "次比较,进行了" << count2 << "次移动!" << endl;
}
int main()
{
int i ;
//int a[10] = { 1, 0, 2, 3, 4, 5, 6, 7, 8, 9 };
int a[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 0, 1};
cout << "排序前的数组是:";
for (i = 0; i < 10; i++)
{
cout << a[i];
}
cout << endl;
BubbleSort(a, 10);
cout << "排序后的结果是:";
for (i = 0; i < 10; i++)
{
cout << a[i];
}
cout << endl;
return 0;
}
可见所谓正宗的冒泡排序跟原来的交换排序的效率是一样的,没有任何差别,到此,这两种算法确实没有区别,但是冒泡排序算法数可以进行优化的,优化后的冒泡排序效率就会得到提高。
对冒泡排序如何进行优化呢?举个栗子吧,比如待排的序列为1023456789,如果使用冒泡排序算法的话会发现,当i=0时(也就是第一趟)以后序列就已经变成了0123456789,已经排好了,剩下的工作只是在进行多余的比较,而没有实质性的操作。也就是说,当前一次循环中已经没有发生交换操作后,序列就是有序的了,后面的循环都将是多余的,所以可以考虑在循环中增加一个标记变量flag,使多余的循环不再被程序执行。
优化后的冒泡排序算法:
#include <iostream>
using namespace std;
void BubbleSort(int k[], int n)
{
int i, j, temp, count1 = 0, count2 = 0, flag; //此处加入flag标志变量
flag = 1; //flag初值设为1
for (i = 0; i < n - 1 && flag; i++)
{
for (j = n - 1; j > i; j--)
{
count1++;
flag = 0; //如果没有进行数据交换,就让flag置为0
if (k[j - 1] > k[j])
{
count2++;
temp = k[j - 1];
k[j - 1] = k[j];
k[j] = temp;
flag = 1; //一旦发生了数据交换,就让flag置1
}
}
}
cout << "总共进行了" << count1 << "次比较,进行了" << count2 << "次移动!" << endl;
}
int main()
{
int i;
int a[10] = { 1, 0, 2, 3, 4, 5, 6, 7, 8, 9 };
//int a[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 0, 1 };
cout << "排序前的数组是:";
for (i = 0; i < 10; i++)
{
cout << a[i];
}
cout << endl;
BubbleSort(a, 10);
cout << "排序后的结果是:";
for (i = 0; i < 10; i++)
{
cout << a[i];
}
cout << endl;
return 0;
}
从结果可以看到,现在把之前的45次比较变成了9+8=17次比较,效率提高了不少!
优化后的冒泡排序,实质上就是避免了对已经有序的序列无意义的循环判断。
冒泡排序算法的复杂度分析:
最好的情况下(序列本身有序):要做(n-1)次比较,移动的次数为0,时间复杂度为O(n)
最坏的情况下(序列本身逆序):要做(n-1)+…3+2+1 = n(n-1)/2次比较,移动的次数为(n-1)+…3+2+1 = n(n-1)/2,时间复杂度为O(n²)
所以,总的时间复杂度为O(n²)