梳排序(Comb sort)是一种由Wlodzimierz Dobosiewicz于1980年所发明的不稳定排序算法,并由Stephen Lacey和Richard Box于1991年四月号的Byte杂志中推广。梳排序是改良自泡沫排序和快速排序,其要旨在于消除乌龟,亦即在阵列尾部的小数值,这些数值是造成泡沫排序缓慢的主因。相对地,兔子,亦即在阵列前端的大数值,不影响泡沫排序的效能。
在泡沫排序中,只比较阵列中相邻的二项,即比较的二项的间距(Gap)是1,梳排序提出此间距其实可大于1,改自插入排序的希尔排序同样提出相同观点。梳排序中,开始时的间距设定为阵列长度,并在循环中以固定比率递减,通常递减率设定为1.3。在一次循环中,梳排序如同泡沫排序一样把阵列从首到尾扫描一次,比较及交换两项,不同的是两项的间距不固定于1。如果间距递减至1,梳排序假定输入阵列大致排序好,并以泡沫排序作最后检查及修正。
递减率
递减率的设定影响着梳排序的效率,原作者以随机数作实验,得到最有效递减率为1.3的。如果此比率太小,则导致一循环中有过多的比较,如果比率太大,则未能有效消除阵列中的乌龟。
亦有人提议用作递减率,同时增加换算表协助于每一循环开始时计算新间距。
变异形式
梳排序-11
设定递减率为1.3时,最后只会有三种不同的间距组合:(9, 6, 4, 3, 2, 1)、(10, 7, 5, 3, 2, 1)、或 (11, 8, 6, 4, 3, 2, 1)。实验证明,如果间距变成9或10时一律改作11,则对效率有明显改善,原因是如果间距曾经是9或10,则到间距变成1时,数值通常不是递增序列,故此要进行几次泡沫排序循环修正。加入此指定间距的变异形式称为梳排序-11(Combsort11)。
混合梳排序和其他排序算法
如同快速排序和合并排序,梳排序的效率在开始时最佳,接近结束时,即进入泡沫排序时最差。如果间距变得太小时(例如小于10),改用诸如插入排序或鸡尾酒排序等算法,则可提升整体效能。
此方法的最大好处是不再需要检查有否进行过交换程序以将排序循环提早结束。
算法实现
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#include<stdbool.h>
void swap(int *a, int *b) //交换两元素的值
{
int t;
t=*a;
*a=*b;
*b=t;
}
void printArray(int a[], int count) //打印数组元素
{
int i;
for(i=0; i<count; i++)
printf("%d ",a[i]);
printf("\n");
}
void combsort(int *a, int size)
{
float shrink_factor = 1.247330950103979; //设置递减率
int gap = size, i;
bool swapped = true;
while ((gap > 1) || swapped) { //当gap=1时,已经基本有序, swapped最后一次遍历排序
if (gap > 1) gap = gap / shrink_factor;
swapped = false;
i = 0;
while ((gap + i) < size) {
if (a[i] > a[i + gap]) {
swap(&a[i],&a[i + gap]);
swapped = true;
}
++i;
}
}
}
int main(void)
{
int a[]={3, 5, 4, 6, 9, 7, 8, 0, 1};
int n=sizeof(a)/sizeof(*a);
printArray(a,n);
combsort(a,n);
printArray(a,n);
return 0;
}
例
假设输入为
- 8 4 3 7 6 5 2 1
目标为将之变成递增排序。 因为输入长度=8,开始时设定间距为8÷1.3≒6, 则比较8和2、4和1,并作交换两次。
- 8 4 3 7 6 5 2 1
- 2 4 3 7 6 5 8 1
- 2 1 3 7 6 5 8 4
第二次循环,更新间距为6÷1.3≒4。比较2和6、1和5,直至7和4,此循环中只须交换一次。
- 2 1 3 7 6 5 8 4
- 2 1 3 4 6 5 8 7
接下来的每次循环,间距依次递减为3 → 2 → 1,
间距=3时,不须交换
- 2 1 3 4 6 5 8 7
间距=2时,不须交换
- 2 1 3 4 6 5 8 7
间距h=1时,交换三次
- 2 1 3 4 6 5 8 7
- 1 2 3 4 6 5 8 7
- 1 2 3 4 5 6 8 7
- 1 2 3 4 5 6 7 8
上例中,共作了六次交换以完成排序。