希尔(shell)排序是D.L.shell于1959年提出的,它属于插入排序方法,是不稳定的排序方法。
我们知道,在直接插入排序算法中,每次插入一个数,使有序序列值增加一个节点,并且对插入下一个数没有提供任何帮助。如果比较相隔较远距离(称为增量)的数,是的数移动时能跨过多个元素,则进行一次比较就可以能消除多个元素交换。
1、希尔排序算法的基本思想:
希尔排序算法先将要排序的一组数按某个增量d分为若干组,每组中记录的下标相差d,对每组中全部元素进行排序,然后用一个较小的增量对它进行再次分组,并对每个新租重新进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。因此希尔排序实质上是一种分组的插入排序方法。
2、希尔排序由于直接插入排序,为什么?
当数组初始状态基本有序时,直接插入排序所需要的比较和移动次数均较少。
当n值较小时,n和n^2的差别也较小,即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度O(n2)差别不大。
在希尔开始时,增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量d逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多。单由于已经按d-1作为距离排过序,数组较接近与有序状态,所以新的一趟排序过程也较快。
因此,希尔排序在效率上较直接插入排序有较大的改进。另外,由于分组的存在,相等元素可能会分在不同组,导致他们的次序可能发生变化,因此希尔排序是不稳定的。
3、思想
希尔排序也是一种插入排序方法,实际上是一种分组插入方法。先取定一个小于n的整数d1作为第一个增量,把表的全部记录分成d1个组,所有距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序;然后,取第二个增量d2(<d1),重复上述的分组和排序,直至所取的增量dt=1(dt<dt-1<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
例如:将 n 个记录分成 d 个子序列:
{ R[0], R[d], R[2d],…, R[kd] }
{ R[1], R[1+d], R[1+2d],…,R[1+kd] }
…
{ R[d-1],R[2d-1],R[3d-1],…,R[(k+1)d-1] }
说明:d=5 时,先从A[d]开始向前插入,判断A[d-d],然后A[d+1]与A[(d+1)-d]比较,如此类推,这一回合后将原序列分为d个组。<由后向前>
4、时间复杂度
最好情况:由于希尔排序的好坏和步长d的选择有很多关系,因此,目前还没有得出最好的步长如何选择(现在有些比较好的选择了,但不确定是否是最好的)。所以,不知道最好的情况下的算法时间复杂度。
最坏情况下:O(N*logN),最坏的情况下和平均情况下差不多。
平均情况下:O(N*logN)
5、稳定性
由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。(有个猜测,方便记忆:一般来说,若存在不相邻元素间交换,则很可能是不稳定的排序。)
6、代码实现
//编程实现希尔插入(Shell)排序
//1、与直接插入排序不同的是:先将数组按大小/2的增量进行分割排序,逐一将增量变小到1为止(h>0),减少插入的次序。
#include<iostream>
using namespace std;
void shell_sort(int a[], int len)
{
int i,j,h,temp;
for (h = len / 2; h > 0; h = h / 2) //控制增量
{
//所选的无序区为h增量之后的数据,直到增量数据循环到数组的最后
for (i = h; i < len; i++) //这个for循环就是前面的直接插入排序
{
temp = a[i];
for (j = i - h; (j >= 0 && temp < a[j]); j = j - h) //有序区与无序区相距增量个长度。j为负数,或者无序区数据大于有序区数据就不循环。
{
a[j + h] = a[j];
}
a[j + h] = temp;
}
}
}
static void print_array(int a[], int len)
{
for (int i = 0; i < len; i++) //循环打印数组的每个元素
cout << a[i] << " ";
cout << endl;
}
void main9mianshiti2()
{
int a[] = { 7, 3, 5, 8, 9, 1, 2, 4, 6 };
cout << "before shell sort: ";
print_array(a, 9);
shell_sort(a, 9); //进行shell排序
cout << "after shell sort: ";
print_array(a, 9);
system("pause");
}
7、测试结果
before shell sort:7 3 5 8 9 1 2 4 6
after shell sort:1 2 3 4 5 6 7 8 9