一、简介
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。
希尔排序是基于插入排序的以下两点性质而提出改进方法的: 1、插入排序在对几乎已经排好序的数据操作时,效率高, 即可以达到线性排序的效率 2、对于大规模乱序数组插入排序很慢,因为它只会交换相邻的元素,因此元素只能一点一点地从数组的一端移动到另一端。
希尔排序简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。(先将整个大数组基本有序,再对大数组来一次插入排序)
思想:使数组中任意间隔为h的元素都是有序的。这样的数组被称为h有序数组。在进行排序时,如果h很大,我们就能将元素移动到很远的地方,为实现更小的h有序创造方便。
二、步骤
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt< d(t-1)< …<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
三、示例
假设有这样一组数[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],如果我们以步长为5开始进行排序,我们可以通过将这列表放在有5列的表中来更好地描述算法,这样他们就应该看起来是这样:
13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10
复制代码
然后我们对每列进行排序:
10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45
复制代码
将上述四行数字,依序接在一起时我们得到: [ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ] 这时10已经移至正确位置了,然后再以3为步长进行排序:
10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45
复制代码
排序之后变为:
10 14 13
25 23 33
27 25 59
39 65 73
45 94 82
94
复制代码
最后以1步长进行排序(此时就是简单的插入排序了)。
四、代码实现
template<typename T> //可以使用整数或浮点数作为元素,如果使用类(class)作为元素则需要重载大于(>)运算符。
void sort(T arr[], int len) {
int gap, i, j;
T temp;
for (gap = len >> 1; gap > 0; gap >>= 1) // gap为当时增量
/* 同时做gap个序列排序 */
for (i = gap; i < len; i++) {
temp = arr[i]; // 保存待插入记录Ri
int j = 0; //有序区中待比较元素的下标,初始时,从有序区中最后一个元素开始比较起
/* 按间隔gap寻找插入点 */
for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap) // 对步长为 gap 的元素组进行排序
arr[j + gap] = arr[j]; // 大记录后移
arr[j + gap] = temp; // 插入记录Ri
}
}
int main()
{
int a[10] = { 3,2,5,21,9,10,7,16,8,20 };
sort(a,10);
for (int i = 0; i < 10; i++) {
cout << a[i] << " ";
}
}
复制代码
五、评价
稳定性:不稳定。我们知道一次插入排序是稳定的,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,即希尔排序中相等数据可能会交换位置。
时间性能:希尔排序的平均时间复杂度为O(nlog2n)
适用范围:中等大小规模表现良好,对规模非常大的数据排序不是最优选择。不适用于链式结构
六、参考资料
排序:直接插入+希尔排序 希尔排序 排序:插入排序之希尔排序(缩小增量排序)
白话经典算法系列之三 希尔排序的实现 中国MOOC大学-程序设计基础-6.问题解决和算法基础3