声明:引用请注明出处http://blog.csdn.net/finish_dream/article/details/70186456
引言
插入排序中有三种具体排序方法,分别是:直接插入排序,二分法插入排序,希尔插入排序。上一篇《排序算法二:二分法插入排序 》中已经介绍了二分法插入排序,这篇文章将主要介绍希尔插入排序。
排序相关的基本概念
排序:将一组杂乱无章的数据按一定的规律顺次排列起来。
- 数据表(data list):它是待排序对象的有限集合。
- 排序码(key):通常对象有多个属性域,即多个数据成员组成,其中有一个属性域,可用来区分对象,作为排序依据。该域即为排序
码,每个数据表用哪个属性域作为排序码,要视具体的应用需要而定。
分类
- 内排序:指在排序期间数据对象全部存放在内存的排序;
- 外排序:指在排序期间全部对象个数太多,不能同时存放在内存,必须根据排序过程的要求,不断在内、外存之间移动的排序。
排序的分析
排序算法的稳定性
如果在对象序列中有两个对象r[i]和r[j] ,它们的排序码k[i]==k[j] 。如果排序前后,对象r[i]和r[j] 的相对位置不变,则称排序算法是稳定的;否则排序算法是不稳定的。
排序算法的评价
时间开销
- 排序的时间开销可用算法执行中的数据比较次数与数据移动次数来衡量。
- 算法运行时间代价的大略估算一般都按平均情况进行估算。对于那些受对象排序码序列初始排列及对象个数影响较大的,需要按最好情况和最坏情况进行估算。
空间开销
算法执行时所需的附加存储。
希尔插入排序
基本思想
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2
C#代码
/// <summary>
/// 希尔插入排序
/// </summary>
/// <param name="Array">待排序数组</param>
/// <param name="N">元素个数</param>
/// <returns></returns>
public static int[] ShellSort(int[] Array, int N)
{
int i, j, Increment;
int Tmp;
for (Increment = N / 2; Increment > 0; Increment /= 2)
{
for (i = Increment; i < N; i++)
{
Tmp = Array[i];
for (j = i; j >= Increment; j -= Increment)
{
if (Array[j - Increment] > Tmp)
{
Array[j] = Array[j - Increment];
}
else
{
break;
}
}
Array[j] = Tmp;
}
}
return Array;
}
希尔插入排序算法分析
- 增量序列的选择
Shell排序的执行时间依赖于增量序列。
好的增量序列的共同特征:
- 最后一个增量必须为1;
- 应该尽量避免序列中的值(尤其是相邻的值)互为倍数的情况。
有人通过大量的实验,给出了目前较好的结果:当n较大时,比较和移动的次数约在nl.25到1.6nl.25之间。
- Shell排序的时间性能优于直接插入排序
希尔排序的时间性能优于直接插入排序的原因:
- 当数据表初态基本有序时直接插入排序所需的比较和移动次数均较少。
- 当n值较小时,n和n2的差别也较小,即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度0(n2)差别不大。
- 在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量di逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但由于已经按di−1作为距离排过序,使数据表较接近于有序状态,所以新的一趟排序过程也较快。
因此,希尔排序在效率上较直接插人排序有较大的改进。
- 稳定性
希尔排序是不稳定的。两个相同关键字在排序前后的相对次序是可能发生变化的。
这个方法对于大的数据集效率其实并不高,但是它是一个对小数量数据表(小于1000)进行排序的最快算法之一。另外,相对使用的内存较少。
完整代码被我放在了Github上,感兴趣的可以下下来看一下https://github.com/Finish-Dream/DSAlgorithm