希尔排序因计算机科学家Donald L.Shell而得名,他在1959年发现了希尔排序算法。希尔排序基于插入排序。但是增加了一个新的特性,大大地提高了插入排序的执行效率。
依靠这个特别的实现机制,希尔排序对于多达几千个数据项的,中等大小规模的数组排序表现良好。希尔排序不像快速排序和其他时间复杂度为O(N*logN)的排序算法那么快,因此对非常大的文件排序,它不是最优选择。但是,希尔排序比选择排序和插入排序这种时间复杂度为O(N*N)的排序算法还是要快的多,并且它非常容易实现:希尔排序算法的代码既短又简单。
它在最坏情况下的执行效率和在平均情况下的执行效率相比没有差很多。一些专家提倡差不多任何排序工作在开始时都可以使用希尔排序算法,若在实际中证明它不够快,再改成诸如快速排序这样更高级的排序算法。
希尔排序通过加大插入排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序,从而使数据项能大跨度的移动。当这些数据项排过一趟序后,希尔排序算法减小数据项的间隔进行排序,一次进行下去。进行这些排序时数据项之间的间隔被称为增量,并且习惯上用字母h来表示。
减小间隔: 举例来说,含有1000个数据项的数组可能先以364为增量,然后以121位增量,以40位增量,以13位增量,以4位增量,最后以1为增量进行希尔排序。用来形成间隔的数列(364,121,40,12,4,1)被称为间隔序列。数列以逆向的形式从1开始,通过递归表达式 h=3*h+1来产生,初始值为1。
在排序算法中,首先在一个短小的循环中使用序列的生成公式来计算出最初的间隔。h值最初被赋为1,然后应用公式h=3*h+1生成序列1,4,13,40,121,364,等等。当间隔大于数组大小的时候这个过程停止。对于一个含有1000个数据项的数组,序列的第七个数字,1093就太大了。因此,使用序列的第六个数字作为最大的数字来开始这个排序过程,作364增量排序,然后每完成一次 排序例程的外部循环,用前面提供的公式的倒推式来减小间隔: h=(h-1)/3。
希尔排序的Java代码:
希尔排序的Java代码不比插入排序的代码复杂多少。只是在插入排序的开始部分,在合适的位置把h赋值为1,并且添加生成间隔序列的公式。
public void ShellSore()
{
int inner,outer;
long temp;
int h=1; //h 的初始值
while(h<=nElems/3) //nElems 为数组长度
h=h*3+1;
while(h>0) //逐步减小h,直到 h=1
{
for(outer=h;outer<nElems;outer++)
{
temp=theArray[outer]; //theArray为待排序数组
inner =outer;
while(inner>h-1 && theArray[inner-h]>=temp)
{
theArray[inner]=theArray[inner-h];
inner=h;
}
theArray[inner]=temp;
}
h=(h-1)/3; //减小h
}
}
尽管希尔排序的算法只需要几行代码来实现,但是根据这个算法也不是很简单的,要看这个算法操作的细节。
其他间隔序列:
选择间隔序列可以称得上是一种魔法。除了上述公式之外,应用其他序列也可取得不同程度的成功。只有一个绝对的条件,就是逐渐缩小的间隔最后一定要等于1,因此最后一趟排序是一次普通的插入排序。
间隔序列中的数字互质通常被认为很重要:也就是说,除了1之外它们没有公约数。这个约束条件使每一趟排序更有可能保持前一趟排序以排好的效果,希尔最初以N/2为间隔的低效率性就是归咎于它没有遵守这个准则。
或许还可以设计出像如上讲述的间隔序列一样好的(甚至更好的)间隔序列。但是不管这个间隔序列是什么,都应该能够快速的计算,而不会降低算法的执行速度。
希尔排序的效率:
迄今为止,除了在一些特殊的情况下,还没有人能够从理论上分析希尔排序的效率。有各种基于试验的评估,估计它的时间级从O(N3/2)到O(N7/6)。
依靠这个特别的实现机制,希尔排序对于多达几千个数据项的,中等大小规模的数组排序表现良好。希尔排序不像快速排序和其他时间复杂度为O(N*logN)的排序算法那么快,因此对非常大的文件排序,它不是最优选择。但是,希尔排序比选择排序和插入排序这种时间复杂度为O(N*N)的排序算法还是要快的多,并且它非常容易实现:希尔排序算法的代码既短又简单。
它在最坏情况下的执行效率和在平均情况下的执行效率相比没有差很多。一些专家提倡差不多任何排序工作在开始时都可以使用希尔排序算法,若在实际中证明它不够快,再改成诸如快速排序这样更高级的排序算法。
希尔排序通过加大插入排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序,从而使数据项能大跨度的移动。当这些数据项排过一趟序后,希尔排序算法减小数据项的间隔进行排序,一次进行下去。进行这些排序时数据项之间的间隔被称为增量,并且习惯上用字母h来表示。
减小间隔: 举例来说,含有1000个数据项的数组可能先以364为增量,然后以121位增量,以40位增量,以13位增量,以4位增量,最后以1为增量进行希尔排序。用来形成间隔的数列(364,121,40,12,4,1)被称为间隔序列。数列以逆向的形式从1开始,通过递归表达式 h=3*h+1来产生,初始值为1。
在排序算法中,首先在一个短小的循环中使用序列的生成公式来计算出最初的间隔。h值最初被赋为1,然后应用公式h=3*h+1生成序列1,4,13,40,121,364,等等。当间隔大于数组大小的时候这个过程停止。对于一个含有1000个数据项的数组,序列的第七个数字,1093就太大了。因此,使用序列的第六个数字作为最大的数字来开始这个排序过程,作364增量排序,然后每完成一次 排序例程的外部循环,用前面提供的公式的倒推式来减小间隔: h=(h-1)/3。
希尔排序的Java代码:
希尔排序的Java代码不比插入排序的代码复杂多少。只是在插入排序的开始部分,在合适的位置把h赋值为1,并且添加生成间隔序列的公式。
public void ShellSore()
{
int inner,outer;
long temp;
int h=1; //h 的初始值
while(h<=nElems/3) //nElems 为数组长度
h=h*3+1;
while(h>0) //逐步减小h,直到 h=1
{
for(outer=h;outer<nElems;outer++)
{
temp=theArray[outer]; //theArray为待排序数组
inner =outer;
while(inner>h-1 && theArray[inner-h]>=temp)
{
theArray[inner]=theArray[inner-h];
inner=h;
}
theArray[inner]=temp;
}
h=(h-1)/3; //减小h
}
}
尽管希尔排序的算法只需要几行代码来实现,但是根据这个算法也不是很简单的,要看这个算法操作的细节。
其他间隔序列:
选择间隔序列可以称得上是一种魔法。除了上述公式之外,应用其他序列也可取得不同程度的成功。只有一个绝对的条件,就是逐渐缩小的间隔最后一定要等于1,因此最后一趟排序是一次普通的插入排序。
间隔序列中的数字互质通常被认为很重要:也就是说,除了1之外它们没有公约数。这个约束条件使每一趟排序更有可能保持前一趟排序以排好的效果,希尔最初以N/2为间隔的低效率性就是归咎于它没有遵守这个准则。
或许还可以设计出像如上讲述的间隔序列一样好的(甚至更好的)间隔序列。但是不管这个间隔序列是什么,都应该能够快速的计算,而不会降低算法的执行速度。
希尔排序的效率:
迄今为止,除了在一些特殊的情况下,还没有人能够从理论上分析希尔排序的效率。有各种基于试验的评估,估计它的时间级从O(N3/2)到O(N7/6)。