1、概述和基本实现
由于是相邻元素比较交换,所以冒泡排序是一种稳定排序算法
在最优的情况下,时间时间复杂度O(n);
平均和最坏情况均是O(n^2)
基本实现:
void bubble_sort(int a[], int len)
{
int i, j, tmp;
for (i = 0; i < len - 1; i++)
for (j = 0; j < len - 1 - i; j++)
{
if (a[j] > a[j+1])
{
tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
}
}
}
2、带标志的冒泡排序
通过设置标志,省去对已有序部分不必要的比较
方法一:设置是否发生交换的标志,只要有一趟没有发生交换,那么就会停止循环扫描,从而减少了扫描的次数。
void bubble_tag( int a[], int n)
{
int i, j, tmp, tag = 1;
for( i=1; i<n && tag; i++ )
{
tag = 0;
for( j=0; j<n-i; j++ )
{
if( a[j] > a[j+1] )
{
tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
tag = 1;
}
}
}
}
方法二:在方法一的基础上进一步改进,标记出前一次最后发生交换的位置,该位置即为有序部分的边界位置,从而省去了每一趟扫描中无谓比较部分
void bubble_flag(int a[], int len)
{
int bound = len - 1;
int flag = 1, i, tmp;
while (flag)
{
flag = 0;
for (i = 0; i <= bound; i++)
{
if (a[i] > a[i+1])
{
tmp = a[i];
a[i] = a[i+1];
a[i+1] = tmp;
flag = i;
}
}
}
}
3、双向冒泡排序
方法一:鸡尾酒排序
方法二:思路和带标志冒泡的方法二一致,只不过变成了两端
void bubble_double_flag(int a[], int len)
{
int left = 0, right = len - 1;
int i, tmp, left_flag = 0, right_flag = len - 1;
while (left_flag <= right_flag)
{
right = left_flag;
for (i = left_flag; i < right_flag; i++)
{
if (a[i] > a[i+1])
{
tmp = a[i];
a[i] = a[i+1];
a[i+1] = tmp;
right = i;
}
}
if (right == left_flag)
break;
right_flag = right;
left = right_flag;
for (i = right_flag; i > left_flag; i--)
{
if (a[i] < a[i-1])
{
tmp = a[i];
a[i] = a[i-1];
a[i-1] = tmp;
left = i;
}
}
if (left == right_flag)
break;
left_flag = left;
}
}
冒泡排序的复杂度决定了它的低效率,上述两种优化也只有在序列一大部分有序的情况下效果明显。
我本机运行时间实测,随机序列的情况下,n为10000时,运行时间就已进入秒的数量级,约1.53s,而当n为100000时,运行时间已145.7s;