首先介绍的是,相对来说比较简单的冒泡排序。
冒泡排序,其基本思想是(如果我们是要从小到大排序):用两个循环(暂且称为内循环和外循环吧),内循环中:从第1位开始依次比较相邻的两个数的大小,
如果前一位比后一位大,则交换两个他们的值,一直比较到最后;这样一趟下来的话,那么最大的那个数就在最后那个位置了。一次内循环就可以选出一个数的
正确位置。所以外循环的次数最多就是该排序数列的大小
代码一:
int bubbling_01(int array[], int size) //基本的冒泡排序
{
int temp;
//一次内循环就可以选出一个数的正确位置,那么一个大小size的数列要完成排序就要size次,其实不然,只要size-1, //因为size-1个数都是正确的 那么剩下的一个就肯定是了,所以可以偷一次懒,
for(int i = 0; i < size - 1; i++)
{
//内层循环只要完成相邻数间的比较就行了。因为每次内循环后就确定了一个数的正确位置,且把它放在后面,所以比较范围是一个逐渐减小 //(size - i)的范围。至于还要减1,是因为我们选择的是array[j]和array[j + 1]的方式比较所以j的范围要小1,不然j+1就越界
for(int j = 0; j < size - i - 1; j++)
{
if(array[j] > array[j + 1]) //升序
{
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
return 0;
}
上面的方法是升序排序,为了让它更灵活,我们可以改一下。
代码二:
typedef int (*compFun)(int a, int b); //定义一个函数指针
int comp_asc(int a, int b) //升序
{
return a - b;
}
int comp_desc(int a, int b) //降序
{
return b - a;
}
int bubbling_02(int array[], int size, compFun comp)
{
int temp;
for(int i = 0; i < size - 1; i++)
{
for(int j = 0; j < size - i - 1; j++)
{
if(comp(array[j], array[j+1]) > 0)
{
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
return 0;
}
int main()
{
int array[10] = {2,1,33,3,6,35,77,42,11,10};
compFun comp = comp_asc; //升序
bubbling_02(array, 10,comp);
for(int i = 0; i < 10; i++)
cout<<array[i]<<",";
cout<<endl;
return 0;
}
代码二中,我们定义了两个分别用于降序和升序的函数,还有一个函数指针,通过该函数指针,我们可以在bubbling_02中更灵活的决定降序还是升序操作。
代码二虽然对代码一中的方法做了改进,但只是灵活性方面的,下面我们做一下性能上的该进。
代码三:
int bubbling_03(int array[], int size, compFun comp)
{
int temp;
bool flag = 0;
for(int i = 0; i < size - 1; i++)
{
flag = 0;
for(int j = 0; j < size - i - 1; j++)
{
if(comp(array[j], array[j+1]) > 0)
{
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
flag = 1;
}
}
if(flag ==0 )
break;
}
return 0;
在代码一和二中,在某些情况下,我们会发现当外循环经过i次后,该数列已经排序好了,但是程序却还会继续执行,直至外循环结束。明显,这是很浪费计算机
资源的,所以我们做了修改,从而得到代码三。其主要思想是:如果某次内循环结束后没发生数据交换,则已经排好序了。所以我们通过观察flag变量有无发生
改变来判断是否跳出循环。
代码四:
int bubbling_03(int array[], int size, compFun comp) //鸡尾排序
{
int temp;
bool flag = 0;
for(int i = 0; i < size - 1; i++)
{
flag = 0;
//从头到尾扫描
for(int j = i; j < size - i - 1; j++)
{
if(comp(array[j], array[j+1]) > 0) {
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
flag = 1;
}
}
//从尾到头扫描
for(int k = size - i - 1; k > i; k--)
{
if(comp(array[k-1], array[k]) > 0)
{
temp = array[k-1];
array[k-1] = array[k];
array[k] = temp;
flag = 1;
}
}
if(flag == 0)
break;
for( int m = 0; m < 10; m++)
cout<<array[m]<<",";
cout<<endl;
}
return 0;
}
代码四所示的方法叫做鸡尾排序,是冒泡的变种。增加了一个内循环。第一个内循环从头扫到尾,得出正确的尾部值,第二个内循环从尾扫到头,得出正确的头部值。并且,还利用了flag来减少不必要的循环。鸡尾酒排序最糟或是平均所花费的次数都是O(n2),但如果序列在一开始已经大部分排序过的话,会接近O(n)。
--------本人菜鸟一只,文中如有错误,望大牛指出^_^