交换排序法 |
鸡尾酒排序 等于是冒泡排序的轻微变形。不同的地方在于从低到高然后从高到低,而冒泡排序则仅从低到高去比较序列里的每个元素。他可以得到比冒泡排序稍微好一点的效能,原因是冒泡排序只从一个方向进行比对(由低到高),每次循环只移动一个项目。
以序列(2,3,4,5,1)为例,鸡尾酒排序只需要访问一次序列就可以完成排序,但如果使用冒泡排序则需要四次。
复杂度
鸡尾酒排序最糟或是平均所花费的次数都是O(n2),但如果序列在一开始已经大部分排序过的话,会接近O(n)。
#include <iostream>
using namespace std;
void swap(int &a,int &b)
{
int temp = a;
a = b;
b = temp;
}
/*
鸡尾酒排序,也就是定向冒泡排序, 鸡尾酒搅拌排序, 搅拌排序 (也可以视作选择排序的一种变形),
涟漪排序, 来回排序 or 快乐小时排序, 是冒泡排序的一种变形。
此算法与冒泡排序的不同处在于排序时是以双向在序列中进行排序。
*/
void cocktail_sort(int A[],int len)
{
bool flag = true;
int i;
int times =1;
while(flag)
{
flag = false;
for(i=0;i<len-times;i++)
{
if(A[i]>A[i+1])
{
swap(A[i],A[i+1]);
flag = true;
}
}
times++;
for(i=len-times;i>1;i--)
{
if(A[i]<A[i-1])
swap(A[i],A[i-1]);
}
}
cout<<times<<endl; //普通的冒泡排序需要17次,而此鸡尾酒法只需8次
}
/*
------奇偶排序法---------
重复两趟扫描。第一趟扫描选择所有的数据项对,a[j]和a[j+1],j是奇数(j=1, 3, 5……)。
如果它们的关键字的值次序颠倒,就交换它们。
第二趟扫描对所有的偶数数据项进行同样的操作(j=2, 4,6……)。
重复进行这样两趟的排序直到数组全部有序。
*/
void oddEvenSort(int A[],int len)
{
bool flag = true;
int times = 0;
int i;
while(flag)
{
flag = false;
for(i=times%2;i<len-1;i=i+2)
{
if(A[i]>A[i+1])
{
swap(A[i],A[i+1]);
flag = true;
}
}
times++;
}
cout<<times++<<endl;
}
/*
梳排序中,
开始时的间距设定为阵列长度,并在循环中以固定比率递减,通常递减率设定为1.3。
在一次循环中,梳排序如同泡沫排序一样把阵列从首到尾扫描一次,比较及交换两项,不同的是两项的间距不固定于1。
如果间距递减至1,梳排序假定输入阵列大致排序好,并以泡沫排序作最后检查及修正。
*/
void combsort(int A[],int len)
{
float shrink_factor = 1.3; //1.247330950103979;
int gap = len;
int i;
while(gap>0)
{
gap = gap/shrink_factor;
for(i=0;i<len-gap;i++)
{
if(A[i]>A[i+gap])
{
swap(A[i],A[i+gap]);
}
}
}
}
/*
-----------地精排序----------
Current array Action to take
[5, 3, 2, 4] a[pos] < a[pos-1], swap:
[3, 5, 2, 4] a[pos] >= a[pos-1], increment pos:
[3, 5, 2, 4] a[pos] < a[pos-1], swap and pos > 1, decrement pos:
[3, 2, 5, 4] a[pos] < a[pos-1], swap and pos <= 1, increment pos:
[2, 3, 5, 4] a[pos] >= a[pos-1], increment pos:
[2, 3, 5, 4] a[pos] < a[pos-1], swap and pos > 1, decrement pos:
[2, 3, 4, 5] a[pos] >= a[pos-1], increment pos:
[2, 3, 4, 5] a[pos] >= a[pos-1], increment pos:
[2, 3, 4, 5] pos == length(a), finished.
*/
void gnomeSort(int A[],int len)
{
int i;
if(A[0]>A[1]) swap(A[0],A[1]);
for(i=1;i<len-1;i++)
{
if(A[i]>A[i+1])
{
swap(A[i],A[i+1]);
i=i-2;
}
}
}
/*
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
步骤为:
①从数列中挑出一个元素,称为 "基准"(pivot),
②重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
③递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。
虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
*/
int Partition(int A[],int low,int high)
{
int pivokey = A[low];
while(low<high)
{
while(low<high && A[high]>=pivokey) high--; //必须要有“=”否则同样大小的数就没人要了!
A[low]=A[high];
while(low<high && A[low]<=pivokey) low++;
A[high]=A[low];
}
A[low]=pivokey;
return low;
}
void quickSort(int A[],int low,int high)
{
int pivotloc;
if(low<high)
{
pivotloc = Partition(A,low,high);
//cout<<"pivotloc:"<<pivotloc<<" A[pivotloc]:"<<A[pivotloc]<<endl;
quickSort(A,low,pivotloc-1);
quickSort(A,pivotloc+1,high);
}
}
int main()
{
//int A[]={15,9,8,1,4,11,7,2,13,16,5,3,6,2,10,14};
int A[]={13,14,94,33,82,25,59,94,65,23,45,27,73,25,39,10,35,54,90,58};
int i;
int len=sizeof(A)/sizeof(int); //在这里 sizeof(array)=80
//oddEvenSort(A,len);
//combsort(A,len);
//gnomeSort(A,len);
//cocktail_sort(A,len);
quickSort(A,0,len-1);
for(i=0;i<len;i++)
{
cout<<A[i]<<" ";
}
cout<<endl;
return 0;
}