希尔排序是特殊的插入排序,直接插入排序每次插入前的遍历步长为1,而希尔排序是将待排序列分为若干个子序列,对这些子序列分别进行直接插入排序,当每个子序列长度为1时,再进行一次直接插入排序时,结果一定是有序的。
为了弄清希尔排序,我们用一个例子来了解。
我们首先要实现一个希尔排序算法的c语言代码,然后再对其进行讲解。
1 void ShellSort(SqList *L)
2 {
3 int i,j;
4 int increment = L->length;
5 do
6 {
7 increment = increment/3 + 1;//增量序列
8 for(i = increment+1; i <= L->length; i++)
9 {
10 if(L->r[i] < L->r[i-increment])
11 {
12
13 //需将L->r[i]插入有序增量子表
14
15 L->r[0] = L->r[i];//暂存在L->r[0]
16
17 for(j = i-increment; j>0 && L->r[0]<L->r[j]; j-=increment)
18 L->r[j+increment] = L->[j];//记录后移,查找插入位置
19
20 L->r[j+increment] = L[0];//插入
21
22 }
23
24 }
25
26 }while(increment > 1);
27
28 }
1.程序开始运行,传入SqList参数值为length=9,r[10]={0, 9, 1, 5, 8, 3, 7, 4, 6, 2},对这个序列进行排序。
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
9 | 1 | 5 | 8 | 3 | 7 | 4 | 6 | 2 |
2.第4行,变量increment就是那个“增量”,我们初始值让它等于待排序的记录数。
3.第5~26行是do循环,终止条件是increment不大于1,即增量为1时就停止循环。
4.第7行为关键,后面再谈。这里执行完后,increment = 9/3 + 1=4.
5.第8~24行是一个for循环,i从4+1=5开始到9结束。
6.第10行,判断L->r[i]与L->r[i-increment]大小,L->r[5]=3小于L->r[i-increment]=L->[1]=9,满足条件,第15行,将L->[5]=3暂存入L->[0]。第17,18行循环将L->[1]=9赋给L->[5],由于循环的增量是j-increment,其实它就循环了一次,此时j=3。第20行,将L->[0]=3赋给L->r[j+increment]=L->[1]=3.即,这段代码就是将第5位的3和第1位的9交换位置。
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
3 | 9 | 1 | 5 | 8 | 3 | 7 | 4 | 6 | 2 |
increment=4 |——9>3,交换——|
7.循环继续,i=6,L->[6]=7>L->r[i-increment]=L->[2]=1,因此不交换两者数据。
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
3 | 3 | 1 | 5 | 8 | 9 | 7 | 4 | 6 | 2 |
increment=4 |——1<7,不交换——|
8.循环继续,i=7,L->[7]=4<L->r[i-increment]=L->[3]=5,交换两者数据。
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
4 | 3 | 1 | 5 | 8 | 9 | 7 | 4 | 6 | 2 |
increment=4 |——1<7,交换——|
9.循环继续,i=8,L->[8]=6<L->r[i-increment]=L->[4]=8,交换两者数据。
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
6 | 3 | 1 | 4 | 8 | 9 | 7 | 5 | 6 | 2 |
increment=4 |——1<7,交换——|
10.循环继续,i=9,L->[9]=2<L->r[i-increment]=L->[5]=9,交换两者数据。注意,第17,18行是循环,此时要比较L->[5]与L->[1]比较大小,因为2<3,所以L->[5]与L->[1]进行数据交换。
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
2 | 3 | 1 | 4 | 6 | 9 | 7 | 5 | 8 | 2 |
increment=4 |——1<7,交换——|
|——3>2,交换——|
最终,第一轮循环结束,此时increment=4,再继续完成下一轮的do循环,得到increment=4/3 + 1=2,在指向上述操作,increment不断减小,直到不大于1,结束循环,这是排序完成。