希尔排序又称为“缩小增量排序”,是一种插入排序算法,是对直接插入排序的一种改进。
思想:
先按照一定的顺序跳着选元素序列进行插入排序,直到整个序列基本有序,再完整地调用一次插入排序。因为前面是跳着选元素的,就是按照一定的增量进行挑选元素,并且,这个增量是逐渐减小的,由于是跳着选的,所以,元素移动地速度就要比直接插入排序来得快,算法的时间复杂度也会明显降低。这里要注意,所取的增量序列中出了最后一个“1”以外,其他数必须是互质的,并且是按降序排列的。为什么呢?如果不是互质,就会出现都某个位置的数重复排序,这是没有必要的。
分解:
对于每一个增量n,原序列被拆分成n个序列:
(a[1] a[n+1] a[2n+1] ...)
(a[2] a[n+2] a[2n+2] ...)
(a[3] a[n+3] a[2n+3] ...)
...
(a[n] a[2n] a[3n] ...)
而上面的每一个序列的排序过程又可以拆分成依次插入排序2个、3个、4个的形式。
(a[1] a[n+1] ) (a[1] a[n+1] a[2n+1] ) (a[1] a[n+1] a[2n+1] ...)
(a[2] a[n+2] ) (a[2] a[n+2] a[2n+2] ) (a[2] a[n+2] a[2n+2] ...)
(a[3] a[n+3] ) (a[3] a[n+3] a[2n+3] ) (a[3] a[n+3] a[2n+3] ...)
...
(a[n] a[2n] ) (a[n-1] a[2n-1] a[3n] ) (a[n-1] a[2n-1] a[3n] ...)
那么,仔细观察上述结构可以发现,上面每个序列的最后一个元素的下标按照从上到下、从左往右的顺序看,是连续的。那么,为了编程实现的方便,对于每一个n,我们都是从a[n+1]开始,一直到数组结束a[end,]找出前面与其间隔n个所有元素进行插入排序即可。这样,就可以很好的简化了整个排序过程的实现。
小插曲:
写代码的时候,一开始是打算传递整个数组进去,以int a[]作为形参,但是,发现,这样必须在返回一个数组作为返回参数。这样子的话,每执行一次,还要将返回值进行
复制到a[],会有额外的开销,所以,考虑使用数组的引用。一开始写成int &a[],一运行,报错了。查了下《C++ Primer》,下标操作比引用具有更高的优先级,所以要在前面加括号,并且必须指定数组的个数,就是写成int (&a)[11]。但是,这样写似乎太过死板了,以后代码就只能够是用于10个数的排序了。怎么办?
最后,发现的利器是模板。就是将数组的元素个数以模板形式传入,这样,这个参数既是指定的,又是可以根据函数的实际调用情况来确定,真的很巧妙。
下面是代码实现:
#include <iostream>
using namespace std;
template<size_t N> void ShellInsert(int (&a)[N],int dk)//希尔插入排序过程
{
int alength = sizeof(a)/sizeof(a[0])-1;
for (int i= dk+1;i<=alength;i++)
{
if (a[i]<a[i-dk])
{ a[0] = a[i];
int j;
for ( j=i-dk;j>0&&a[0]<a[j];j-=dk)
{
a[j+dk] = a[j];
}
a[j+dk] = a[0];
}
}
}
template<size_t N> void ShellSort(int (&a)[N],int dklist[],int listlen)
{
for (int i=0;i<listlen;i++)
{
ShellInsert(a,dklist[i]);
}
}
void main()
{
int a[] ={0,2,5,6,9,8,4,3,1,6,0};//a[0] 是哨兵
int dklist[] = {5,3,1};
ShellSort(a,dklist,3);
for (int i = 1;i<11;i++)
{
cout<<a[i]<<endl;
}
}
当然,也可以用直接传入一个指针的形式来传入参数的,就是int *a。
#include <iostream>
using namespace std;
void ShellInsert(int *a,int dk,int alength)//希尔插入排序过程
{
//int alength = 10;//sizeof(a)/sizeof(a[0])-1;
for (int i= dk+1;i<=alength;i++)
{
if (a[i]<a[i-dk])
{
a[0] = a[i];
int j;
for ( j=i-dk;j>0&&a[0]<a[j];j-=dk)
{
a[j+dk] = a[j];
}
a[j+dk] = a[0];
}
}
}
void ShellSort(int *a,int alength,int dklist[],int listlen)
{
for (int i=0;i<listlen;i++)
{
ShellInsert(a,dklist[i],alength);
}
}
void main()
{
int a[] ={0,2,5,6,9,8,4,3,1,6,0};//a[0] 是哨兵
int dklist[] = {5,3,1};
ShellSort(a,10,dklist,3);
for (int i = 1;i<11;i++)
{
cout<<a[i]<<endl;
}
}