一、简介
希尔排序名称源于它的发明者 Donald Shell,又称缩小增量排序。本质上讲,希尔排序是直接插入排序的一种改进,减少了复制次数,提升了速度。
其原理为选定一个增量 increment,使得数组中间隔为 increment 的元素放在一个子序列中,对每个子序列进行插入排序。然后逐渐缩小 increment,重复上述划分和排序,直至 increment 缩小到 1 时完成排序。
二、图解排序过程
其中相同颜色的元素表示在同一个子序列中的元素,他们的增量相同。
三、时间复杂度
希尔排序的时间复杂度与增量序列的选取有关。使用希尔增量(即增量为长度除以2,直至1)时最坏为 O(n2),而使用 Hibbard 增量(即增量为 1, 3, 7, …, 2k - 1)时最坏为 O(n3/2)。
希尔排序的时间复杂度下界为 n * lg n,并没有快速排序等时间复杂度为 O(n * lg n) 的算法快。但是比直接插入排序等时间复杂度为 O(n2) 的算法快不少。因此它适合于规模不算大的集合排序。
四、代码实现
关于希尔排序增量的选择,本例使用 Hibbard 增量。当然也有人提出都取奇数作为增量,亦或是要求增量互质,增量序列的选择直接影响希尔排序的性能,但是这里不再做过多讨论了。
public class Main {
public static void main(String[] args) {
int[] arr = new int[] { 81, 94, 11, 96, 12, 35, 17, 95, 28, 58, 41, 75, 15 };
shellSort(arr);
printArray(arr);
}
public static void shellSort(int[] arr) {
int k = 0;
int length = arr.length;
while (length > 1) {
length >>= 1;
k++; // 循环确定 k 的最大取值
}
// Hibbard增量:1, 3, 7, ..., 2^k-1
for (; k > 0; k--) {
int increment = 1 << k - 1;
// insert sort 增强版
for (int i = increment; i < arr.length; i++) {
for (int j = i; j >= increment; j -= increment) {
if (arr[j] < arr[j - increment]) {
int temp = arr[j];
arr[j] = arr[j - increment];
arr[j - increment] = temp;
} else {
break;
}
}
}
}
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
}
执行结果:
11 12 15 17 28 35 41 58 75 81 94 95 96