希尔排序是插入排序的升级版,简单说,希尔排序就是数据分组然后再进行插入排序。
正因为希尔排序的存在,一般将插入排序称为直接插入排序。
很早就看过希尔排序,自己似乎也动手实现过这个算法。但在今天温习Braian.W.Kernihan、Dennis M.Ritchie合著的《C程序设计语言》时候,发现书上有一个希尔排序的程序(下),非常简洁,甚至让我有些迷糊。
/*
shell sort:grouping and insert sorting
dennis m. ritchie
*/
void shell_sort(int a[], int len)
{
int gap, i, j, temp;
for(gap = len/2; gap > 0; gap /= 2)
{
for(i = gap; i < len; i++)
{
for(j = i-gap; j>=0 && a[j]>a[j+gap]; j -= gap)
{
temp = a[j];
a[j] = a[j+gap];
a[j+gap] = temp;
}
}
}
}
在最里层的for循环中,我看不出来这是插入排序,与自己写的插入排序(下)相差太远了。
/*
insert sort
the worst cal o(n^2), the best cal is o(n)
*/
void insert_sort(int a[], int len)
{
int i,j,temp;
for(i = 1; i < len; i++)
{
temp = a[i];
for(j = i-1; j >= 0; j--)
{
if(a[j] > temp)
{
a[j+1] = a[j];
}
else
{
a[j+1] = temp;
break;
}
}
}
}
相应地,我的希尔排序程序为:
/*
shell sort:grouping and insert sorting
*/
void shell_sort_house(int a[], int len)
{
int gap, i, j, k, temp;
for(gap = len/2; gap > 0; gap /= 2)
{
for(i = 0; i < gap; i++)
{
for(j = 0; j*gap < len; j++)
{
temp = a[i+j*gap];
for(k = j-1; k >= 0; k--)
{
if( a[i+k*gap] > temp)
{
a[i+(k+1)*gap] = a[i+k*gap];
}
else
{
a[i+(k+1)*gap] = temp;
break;
}
}
}
}
}
}
对比一下,不得不说Dennis大神的程序比我的简洁N倍。
后来上网查阅资料,发现书中的写法原来是将“后移”操作改成了“交换”,本质上,还是插入排序。在http://blog.csdn.net/morewindows/article/details/6665714
一文中有详细介绍。
本以为温习《C程序设计语言》一书只是用来打发时间的,越看越发现自己还有许多不懂的地方。
最后附上一个完整的测试程序清单:
#include <stdio.h>
void print_array(int *list, int len)
{
int i;
for (i = 0; i < len; ++i)
{
printf("%d\t", *(list+i));
}
printf("\n");
}
/*
insert sort
the worst cal o(n^2), the best cal is o(n)
*/
void insert_sort(int a[], int len)
{
int i,j,temp;
for(i = 1; i < len; i++)
{
temp = a[i];
for(j = i-1; j >= 0; j--)
{
if(a[j] > temp)
{
a[j+1] = a[j];
}
else
{
a[j+1] = temp;
break;
}
}
}
}
/*
shell sort:grouping and insert sorting
dennis m. ritchie
*/
void shell_sort(int a[], int len)
{
int gap, i, j, temp;
for(gap = len/2; gap > 0; gap /= 2)
{
for(i = gap; i < len; i++)
{
for(j = i-gap; j>=0 && a[j]>a[j+gap]; j -= gap)
{
temp = a[j];
a[j] = a[j+gap];
a[j+gap] = temp;
}
}
}
}
/*
shell sort:grouping and insert sorting
*/
void shell_sort_house(int a[], int len)
{
int gap, i, j, k, temp;
for(gap = len/2; gap > 0; gap /= 2)
{
for(i = 0; i < gap; i++)
{
for(j = 0; j*gap < len; j++)
{
temp = a[i+j*gap];
for(k = j-1; k >= 0; k--)
{
if( a[i+k*gap] > temp)
{
a[i+(k+1)*gap] = a[i+k*gap];
}
else
{
a[i+(k+1)*gap] = temp;
break;
}
}
}
}
}
}
int main(int argc, char **argv)
{
int ary[] = {2, 3, 4, 7, 1, 6, 8, 2, 10, 5, 12, 2, 3, 33, 12, 45, 12, 123, 44, 12, 32, 19, 29, 333};
int len = sizeof(ary)/sizeof(int);
print_array(ary, len);
//insert_sort(ary, len);
shell_sort_house(ary, len);
print_array(ary, len);
return 0;
}
算法复杂度分析:
稳定性:相同记录,经过排序后,它们之间的相对位置保持不变,此类排序算法就是稳定的。常见的稳定排序算法有插入排序、冒泡排序、归并排序。这些算法之所以稳定,来自于它们算法本身,遇见相同记录时,保持原样,不作处理,所以这些相同记录的相对位置仍不变。相对而言,直接选择排序、堆排序、shell排序、快速排序都是不稳定算法。
时间复杂度,和空间复杂度是算法复杂度的另外两个指标,从上文提到的排序算法来看,冒泡法的速度最慢,希尔排序最高。空间复杂度基本差不多。