为什么希尔排序效率高
插入排序的两个特点:
(1)元素越少排序越快;
(2)元素有序程度越高排序越快。
希尔排序充分利用了这两个特点使用插入排序算法对数据进行排序。
使用插入排序时需要选用一个递增序列,该递增数列的第一个元素必须是1。
递增序列的元素将作为希尔排序的增量。希尔排序增量对待排序的元素进行分组。
第一次循环,使用递增序列的倒数第一个值作为增量对元素进行分组,然后使用插入排序对每个分组进行排序;
第二次循环,再使用递增数列的倒数第二个值作为增量对元素进行分组;
直到递增序列的最后一个值。
递增序列的第一个值是1,也就是说,最后一次循环时,将使用1作为增量,也就是将全部元素化为一组,然后使用插入排序进行最后的排序。
在这个过程中,可以看到,在前面的循环中增量较大,因此每个分组中的元素较少。虽然此时数组的有序度比较差,但元素较少所以插入排序效率依然很高。
随着循环的次数增多,增量越来越小,分组中的元素越来越多,但是元素的有序度越来越高,所以插入排序的效率依然很高。
综上所述,希尔排序就是充分利用插入排序的特点,然后构造出高效插入排序算法的所需特殊条件,使得插入排序算法可以高效进行。
希尔排序轨迹分析
下面以《算法(第四版)》中的算法2.3为例,讲解希尔排序的基本原理。
public class Shell{
public static void sort(Comparable[] a) {
int N = a.length;
int h = 1;
while (h < N / 3) {
h = 3 * h + 1; //1, 4, 13
}
while (h >= 1) {
for (int i = h; i < N; i++) {
for (int j = i; j >= h && less(a[j], a[j - h]); j -= h) {
exch(a, j, j - h); //交换元素
}
}
h = h / 3;
}
}
}
在《算法(第四版)》中的算法2.3中,作者使用了递增序列[1, 4, 13, 40, 121, 364, 1093,...]。
待排序的元素序列为 [S H E L L S O R T E X A M P L E],一共16个元素。
当增量h=13时,分为3组(以元素索引代替元素):[0,13],[1,14],[2,15]。
比较的元素 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
S | H | E | L | L | S | O | R | T | E | X | A | M | P | L | E | ||
i=13 | [0, 13] | P | H | E | L | L | S | O | R | T | E | X | A | M | S | L | E |
i=14 | [1, 14] | P | H | E | L | L | S | O | R | T | E | X | A | M | S | L | E |
i=15 | [2, 15] | P | H | E | L | L | S | O | R | T | E | X | A | M | S | L | E |
红色代表两个元素交换了位置,蓝色代表元素只是进行了比较但没交换位置。
当增量h=4时,分为4组:[0, 4, 8, 12], [1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15]。
比较的元素 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
i=4 | [0, 4] | L | H | E | L | P | S | O | R | T | E | X | A | M | S | L | E |
i=5 | [1, 5] | L | H | E | L | P | S | O | R | T | E | X | A | M | S | L | E |
i=6 | [2, 6] | L | H | E | L | P | S | O | R | T | E | X | A | M | S | L | E |
i=7 | [3, 7] | L | H | E | L | P | S | O | R | T | E | X | A | M | S | L | E |
i=8 | [0, 4, 8] | L | H | E | L | P | S | O | R | T | E | X | A | M | S | L | E |
i=9 | [1, 5, 9] | L | E | E | L | P | H | O | R | T | S | X | A | M | S | L | E |
i=10 | [2, 6, 10] | L | E | E | L | P | H | O | R | T | S | X | A | M | S | L | E |
i=11 | [3, 7, 11] | L | E | E | A | P | H | O | L | T | S | X | R | M | S | L | E |
i=12 | [0, 4, 8, 12] | L | E | E | A | M | H | O | L | P | S | X | R | T | S | L | E |
i=13 | [1, 5, 9, 13] | L | E | E | A | M | H | O | L | P | S | X | R | T | S | L | E |
i=14 | [2, 6, 10, 14] | L | E | E | A | M | H | L | L | P | S | O | R | T | S | X | E |
i=15 | [3, 7, 11, 15] | L | E | E | A | M | H | L | E | P | S | O | L | T | S | X | R |
i = 4, i = 8, i = 12时,是对分组[0, 4, 8, 12]进行插入排序;
i = 5, i = 9, i = 13时,是对分组[1, 5, 9, 13]分组进行排序;
i = 6, i = 10, i = 14时,是对分组[2, 6, 10, 14]分组进行排序;
i = 7, i = 11, i = 15时,是对分组[3, 7, 11, 15]分组进行排序。
由此可以看出,对各个分组的排序是交替进行的,并不是分组进行的。
当h=1时,将全部元素分为1组:[0 - 15]。
比较的元素 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | |
i=1 | [0 - 1] | E | L | E | A | M | H | L | E | P | S | O | L | T | S | X | R |
i=2 | [0 - 2] | E | E | L | A | M | H | L | E | P | S | O | L | T | S | X | R |
i=3 | [0 - 3] | A | E | E | L | M | H | L | E | P | S | O | L | T | S | X | R |
i=4 | [0 - 4] | A | E | E | L | M | H | L | E | P | S | O | L | T | S | X | R |
i=5 | [0 - 5] | A | E | E | H | L | M | L | E | P | S | O | L | T | S | X | R |
i=6 | [0 - 6] | A | E | E | H | L | L | M | E | P | S | O | L | T | S | X | R |
i=7 | [0 - 7] | A | E | E | E | H | L | L | M | P | S | O | L | T | S | X | R |
i=8 | [0 - 8] | A | E | E | E | H | L | L | M | P | S | O | L | T | S | X | R |
i=9 | [0 - 9] | A | E | E | E | H | L | L | M | P | S | O | L | T | S | X | R |
i=10 | [0 - 10] | A | E | E | E | H | L | L | M | O | P | S | L | T | S | X | R |
i=11 | [0 - 11] | A | E | E | E | H | L | L | L | M | O | P | S | T | S | X | R |
i=12 | [0 - 12] | A | E | E | E | H | L | L | L | M | O | P | S | T | S | X | R |
i=13 | [0 - 13] | A | E | E | E | H | L | L | L | M | O | P | S | S | T | X | R |
i=14 | [0 - 14] | A | E | E | E | H | L | L | L | M | O | P | S | S | T | X | R |
i=15 | [0 - 15] | A | E | E | E | H | L | L | L | M | O | P | R | S | S | T | X |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
当h=1时,就是普通的插入排序算法。虽然h=1时,元素较多,但是有序度已经较高,所以排序依然很快。
可以让其分组进行,而不是交替进行。可以这样修改:
public class Shell {
public static void sort(Comparable[] a) {
int N = a.length;
int h = 1;
while (h < N / 3) {
h = 3 * h + 1;
}
while (h >= 1) {
for(int k = 0; k < h && h +k < N; k++)
{
for (int i = h +k; i < N; i += h) {
for (int j = i; j >= h && less(a[j], a[j - h]); j -= h) {
exch(a, j, j - h);
}
}
}
h = h / 3;
}
}
上面代码的改动:
1. 将 i++ 修改为 i +=h,使得每次循环跳过h个元素,挑出一组元素集中进行插入排序;
2. 添加一个for循环控制 i 的取值。
参考资料:1. 《算法(第四版)》 Robert Sedgewick著
2. https://blog.csdn.net/qq_39207948/article/details/80006224
3. .https://blog.csdn.net/weixin_37818081/article/details/79202115