鸡尾酒排序
鸡尾酒排序这个名字听起来很文艺,其实就是双向冒泡排序,它是冒泡排序的一种变形。冒泡排序是往一个方向冒泡,鸡尾酒排序则是先向一个方向,然后向另一个方向,来回冒泡。
原理
鸡尾酒排序的基本原理也是通过不断交换相邻的两个元素来让小的元素越来越靠前,大的元素越来越靠后。步骤如下:假设有n个元素,
- 先考虑内层循环。
- 比较第一个和第二个元素,若第一个大,则交换,否则不交换;
- 比较第二个和第三个元素,若第二个大,则交换,否则不交换;
- 比较第三个和第四个元素,若第三个大,则交换,否则不交换;
- 以此类推,直到最后一个元素。此时最大的元素会被交换到最后。
- 比较第(n-1)个和第(n-2)个元素,若第(n-2)个大,则交换,否则不交换;
- 比较第(n-2)个和第(n-3)个元素,若第(n-3)个大,则交换,否则不交换;
- 比较第(n-3)个和第(n-4)个元素,若第(n-4)个大,则交换,否则不交换;
- 以此类推,直到第一个元素。此时最小的元素会被交换到最前。
这样经过内层循环一遍之后,最大的元素会被交换到最后,同时最小的元素会被交换到最前。
- 再考虑外层循环。外层循环的每一次,内层循环会走一遍。第一遍的时候内层循环有n个元素,第一遍走完,第二遍的时候就只有(n-2)个元素了,以此类推,直到没有元素。
实现
按照这个原理我们来用代码实现。这时问题又来了:是否可以像冒泡排序一样,也不需要另外开辟空间,直接在原址上排序呢?答案同样是可以。因为只需要两个元素的交换操作,不需要额外的空间。
下面就是用C语言实现的代码。
- 要排序的数组a有n个元素。
- 用begin和end来指示内层循环所需进行的范围,冒一次泡,就调整一下下次冒泡范围。
void cocktail_sort(int a[], int n)
{
int i,tmp;
int begin=0;
int end=n-1;
while(begin<end) {
// swap the largest to the end
for(int i=begin; i<end; i++) {
if(a[i]>a[i+1]) {
tmp = a[i];
a[i]= a[i+1];
a[i+1] = tmp;
}
}
end--;
// swap the smallest to the beginning
for(int i=end; i>begin; i--) {
if(a[i]<a[i-1]) {
tmp = a[i];
a[i]= a[i-1];
a[i-1] = tmp;
}
}
begin++;
}
}
为了验证此函数的效果,加上了如下辅助代码,对3个数组进行排序,运行结果在最后,可见排序成功。
#include <stdio.h>
#include <stdlib.h>
#define SIZE_ARRAY_1 5
#define SIZE_ARRAY_2 6
#define SIZE_ARRAY_3 20
void cocktail_sort(int a[], int n);
void show_array(int a[], int n);
void main()
{
int array1[SIZE_ARRAY_1]={1,4,2,-9,0};
int array2[SIZE_ARRAY_2]={10,5,2,1,9,2};
int array3[SIZE_ARRAY_3];
for(int i=0; i<SIZE_ARRAY_3; i++) {
array3[i] = (int)((40.0*rand())/(RAND_MAX+1.0)-20);
}
printf("Before sort, ");
show_array(array1, SIZE_ARRAY_1);
cocktail_sort(array1, SIZE_ARRAY_1);
printf("After sort, ");
show_array(array1, SIZE_ARRAY_1);
printf("Before sort, ");
show_array(array2, SIZE_ARRAY_2);
cocktail_sort(array2, SIZE_ARRAY_2);
printf("After sort, ");
show_array(array2, SIZE_ARRAY_2);
printf("Before sort, ");
show_array(array3, SIZE_ARRAY_3);
cocktail_sort(array3, SIZE_ARRAY_3);
printf("After sort, ");
show_array(array3, SIZE_ARRAY_3);
}
void show_array(int a[], int n)
{
if(n>0)
printf("This array has %d items: ", n);
else
printf("Error: array size should bigger than zero.\n");
for(int i=0; i<n; i++) {
printf("%d ", a[i]);
}
printf("\n");
}
运行结果:
Before sort, This array has 5 items: 1 4 2 -9 0
After sort, This array has 5 items: -9 0 1 2 4
Before sort, This array has 6 items: 10 5 2 1 9 2
After sort, This array has 6 items: 1 2 2 5 9 10
Before sort, This array has 20 items: 13 -4 11 11 16 -12 -6 10 -8 2 0 5 -5 0 18 16 5 8 -14 4
After sort, This array has 20 items: -14 -12 -8 -6 -5 -4 0 0 2 4 5 5 8 10 11 11 13 16 16 18
分析
时间复杂度
从代码可见,用了for和while两层循环,且每个循环都是 n 的量级,所以鸡尾酒排序的时间复杂度为 O(n^2)。
空间复杂度
因为鸡尾酒排序直接在原址进行,不需要另外的空间,所以空间复杂度是 O(1)。