基本思想
先取一个小于n的整数d1作为第一个增量&#xff0c;把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序&#xff1b;然后&#xff0c;取第二个增量d2 比较相隔较远距离&#xff08;称为增量&#xff09;的数&#xff0c;使得数移动时能跨过多个元素&#xff0c;则进行一次比[1]较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组&#xff0c;每组中记录的下标相差d.对每组中全部元素进行排序&#xff0c;然后再用一个较小的增量对它进行&#xff0c;在每组中再进行排序。当增量减到1时&#xff0c;整个要排序的数被分成一组&#xff0c;排序完成。 一般的初次取序列的一半为增量&#xff0c;以后每次减半&#xff0c;直到增量为1。 假设待排序文件有10个记录&#xff0c;其关键字分别是&#xff1a; 四九&#xff0c;三八&#xff0c;六五&#xff0c;九七&#xff0c;七六&#xff0c;一三&#xff0c;二七&#xff0c;四九&#xff0c;五五&#xff0c;零四。 增量序列的取值依次为&#xff1a; 希尔排序属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序。 排序过程&#xff1a;先取一个正整数d1 [3] 三趟结果 零四 一三 二七 三八 四九 四九 五五 六五 七六 九七 Shell排序的算法实现: 1&#xff0e; 不设监视哨的算法描述 void ShellPass(SeqList R&#xff0c;int d) {//希尔排序中的一趟排序&#xff0c;d为当前增量 for(i&#61;d&#43;1;i<&#61;n&#xff1b;i&#43;&#43;) //将R[d&#43;1&#xff0e;&#xff0e;n]分别插入各组当前的有序区 if(R[ i ].key R[0]&#61;R[i];j&#61;i-d&#xff1b; //R[0]只是暂存单元&#xff0c;不是哨兵 do {//查找R的插入位置 R[j&#43;d]&#61;R[j]&#xff1b; //后移记录 j&#61;j-d&#xff1b; //查找前一记录 }while(j>0&&R[0].key R[j&#43;d]&#61;R[0]&#xff1b; //插入R到正确的位置上 } //endif 不需要大量的辅助空间&#xff0c;和归并排序一样容易实现。希尔排序是基于插入排序的一种算法&#xff0c; 在此算法基础之上增加了一个新的特性&#xff0c;提高了效率。希尔排序的时间复杂度与增量序列的选取有关&#xff0c;例如希尔增量时间复杂度为O(n²)&#xff0c;而Hibbard增量的希尔排序的时间复杂度为O(N(3/2))&#xff0c;但是现今仍然没有人能找出希尔排序的精确下界。希尔排序没有快速排序算法快 O(N*(logN))&#xff0c;因此中等大小规模表现良好&#xff0c;对规模非常大的数据排序不是最优选择。但是比O(N^2)复杂度的算法快得多。并且希尔排序非常容易实现&#xff0c;算法代码短而简单。 此外&#xff0c;希尔算法在最坏的情况下和平均情况下执行效率相差不是很多&#xff0c;与此同时快速排序在最坏的情况下执行的效率会非常差。专家们提倡&#xff0c;几乎任何排序工作在开始时都可以用希尔排序&#xff0c;若在实际使用中证明它不够快&#xff0c;再改成快速排序这样更高级的排序算法. 本质上讲&#xff0c;希尔排序算法的一种改进&#xff0c;减少了其复制的次数&#xff0c;速度要快很多。 原因是&#xff0c;当N值很大时数据项每一趟排序需要的个数很少&#xff0c;但数据项的距离很长。当N值减小时每一趟需要和动的数据增多&#xff0c;此时已经接近于它们排序后的最终位置。 正是这两种情况的结合才使希尔排序效率比插入排序高很多。 1&#xff0e;增量序列的选择 Shell排序的执行时间依赖于增量序列。 好的增量序列的共同特征&#xff1a; ① 最后一个增量必须为1&#xff1b; ② 应该尽量避免序列中的值(尤其是相邻的值)互为倍数的情况。 有人通过大量的实验&#xff0c;给出了较好的结果&#xff1a;当n较大时&#xff0c;比较和移动的次数约在nl.25到1.6n1.25之间。 2&#xff0e;Shell排序的时间性能优于直接插入排序 希尔排序的时间性能优于直接插入排序的原因&#xff1a; ①当文件初态基本有序时直接插入排序所需的比较和移动次数均较少。 ②当n值较小时&#xff0c;n和n2的差别也较小&#xff0c;即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度0(n2)差别不大。 ③在希尔排序开始时增量较大&#xff0c;分组较多&#xff0c;每组的记录数目少&#xff0c;故各组内直接插入较快&#xff0c;后来增量di逐渐缩小&#xff0c;分组数逐渐减少&#xff0c;而各组的记录数目逐渐增多&#xff0c;但由于已经按di-1作为距离排过序&#xff0c;使文件较接近于有序状态&#xff0c;所以新的一趟排序过程也较快。 因此&#xff0c;希尔排序在效率上较直接插入排序有较大的改进。 希尔排序是按照不同步长对元素进行插入排序&#xff0c;当刚开始元素很无序的时候&#xff0c;步长最大&#xff0c;所以插入排序的元素个数很少&#xff0c;速度很快&#xff1b;当元素基本有序了&#xff0c;步长很小&#xff0c;插入排序对于有序的序列效率很高。所以&#xff0c;希尔排序的时间复杂度会比o(n^2)好一些。由于多次插入排序&#xff0c;我们知道一次插入排序是稳定的&#xff0c;不会改变相同元素的相对顺序&#xff0c;但在不同的插入排序过程中&#xff0c;相同的元素可能在各自的插入排序中移动&#xff0c;最后其稳定性就会被打乱&#xff0c;所以shell排序是不稳定的。 举例阐述&#xff1a; 例如&#xff0c;假设有这样一组数[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ]&#xff0c;如果我们以步长为5开始进行排序&#xff0c;我们可以通过将这列表放在有5列的表中来更好地描述算法&#xff0c;这样他们就应该看起来是这样&#xff1a; 13 14 94 33 82 然后我们对每列进行排序&#xff1a; 10 14 73 25 23 将上述四行数字&#xff0c;依序接在一起时我们得到&#xff1a;[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].这时10已经移至正确位置了&#xff0c;然后再以3为步长进行排序&#xff1a; 10 14 73 排序之后变为&#xff1a; 10 14 13 最后以1步长进行排序&#xff08;此时就是简单的插入排序了&#xff09;。 图示&#xff1a; C&#43;&#43;代码实现&#xff1a; #include int i, j, temp; int main() int a[] &#61; {5, 4, 3, 2, 1}; int key;该方法实质上是一种分组插入方法引
排序过程
缩小增量
Shell排序
优劣
时间性能
25 59 94 65 23
45 27 73 25 39
10
13 27 94 33 39
25 59 94 65 82
45
25 23 13
27 94 33
39 25 59
94 65 82
45
25 23 33
27 25 59
39 65 73
45 94 82
94
void shellsort(int *data, size_t size);
int gap &#61; 0;
{
const int n &#61; 5;
shellsort(a,5);
/* while (gap<&#61;n)
{
gap &#61; gap * 3 &#43; 1;
}
while (gap > 0)
{
for ( i &#61; gap; i
j &#61; i - gap;
temp &#61; a[i];
while (( j >&#61; 0 ) && ( a[j] > temp ))
{
a[j &#43; gap] &#61; a[j];
j &#61; j - gap;
}
a[j &#43; gap] &#61; temp;
}
gap &#61; ( gap - 1 ) / 3;
} */
for(i&#61;0;i<5;i&#43;&#43;)
printf("%d\n",a[i]);
}
void shellsort(int *data, size_t size)
{
int j &#61; 0;
for (gap &#61; size / 2; gap > 0; gap /&#61; 2)
for ( i &#61; gap; i
key &#61; data[i];
for( j &#61; i -gap; j >&#61; 0 && data[j] > key; j -&#61;gap)
{
data[j&#43;gap] &#61; data[j];
}
data[j&#43;gap] &#61; key;
}
}