希尔排序
希尔排序步骤
对于一个序列,首先将它按一个数字做间隔分组,比如我们提取第一个数字81,然后间隔5个位置,取到35,再间隔5个位置取到41,再间隔5个位置超过了数组的边界。81,35,41(上图第一行中蓝色部分),对这三个数字构成的序列做一个插入排序=》35,41,81。
然后在上面排序后的数组的基础上,缩小间隔,这次每隔3个间隔取一个数,构成序列:35,28,75,58,95(上图第二行的蓝色部分),再做插入排序。
最后1间隔,对所有的数做插入排序。
会发现在最后做1间隔排序的时候,整个序列已经基本有序。这对插入排序非常友好,因为如果序列基本有序,那就意味着逆序对较少,插入排序的效率接近O(n)。
这就是希尔排序,我们设置一个间隔序列5-3-1,逐步使序列变得有序。并且发现做完5间隔的排序后,再做3间隔排序,并不会对5间隔的排序产生影响,也就是:
K-间隔有序的序列,在执行K-1间隔排序后,仍然是K间隔有序的。
有一个最坏的情况:
做8间隔排序,发现序列已经有序,无序改变;
做4间隔排序,发现序列已经有序,无序改变;
做2间隔排序,发现序列已经有序,无序改变;
最后不得不做1间隔排序,即所有的元素做插入排序。
也就是说前面的N间隔排序没有起到效果。说明:
增量元素不互质,则小增量可能根本起不到作用。
因此,有人研究出一些经典的增量序列:
发现时间复杂度的证明很困难。
Java代码实现
public void sort(int[] arr) {
// 定义一个增量序列
int[] seqs = {5, 3, 1};
for (int seq : seqs) {
// 下面就是插入排序
// 默认第一个数是有序的
for (int i = 1; i < arr.length; i++) {
int temp = i;
// 每次新进来一个数,把这个数和左边已经排好序的部分比较
for (int j = i; j > 0; j -= seq) {// 注意,这里不是j--,而是减去一个序列数
// 如果新进来的数比相邻左边的数小,那就把左边的数往右挪一位
if (arr[j] < arr[j - 1]) {
arr[j] = arr[j - 1];
temp = j - 1;
}
}
// 找到空位,插入
if (temp != i) {
arr[temp] = i;
}
}
}
}
优点:
数据量大的时候效果好,使用特定增量序列的最坏时间复杂度比简单排序O(n^2)好。
缺点:
不是稳定排序。