1. 引入
插入排序存在的问题: 现在有这么一个数组,arr={2,3,4,5,6,1};现在需要插入的数 1 (最小),过程是:
{2,3,4,5,6,6} → {2,3,4,5,5,6} → {2,3,4,4,5,6} → {2,3,3,4,5,6} → {2,2,3,4,5,6} → {1,2,3,4,5,6}
[结论] 当需要插入的数是较小的数时,后移的次数明显增多,对效率有影响。
2. 概述
- 希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。
- 基本思想
- 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序
- 随着增量逐渐减少,每组包含的关键词越来越多
- 当增量减至1时,整个文件恰被分成一组,算法便终止
- 较之于〈插入排序〉效率高的原因:
- 在间隔大的时候,挪动的次数少
- 在间隔小的时候,挪动的距离短
3. 标配
- 平均时间复杂度:O(n^1.3)
- 最坏时间复杂度:O(n^2)
- 最好时间复杂度:O(n)
- 空间复杂度:O(1),内排序
- 不稳定 // 比方说有俩 1,排之前
..., a1, ..., b1, ...
排之后可能会出现b1, a1, ...
4. 举例
- 希尔排序的思想是使数组中任意间隔为 h 的元素都是有序的。这样的数组被称为h 有序数组。换句话说,一个h 有序数组就是h 个互相独立的有序数组编织在一起组成的一个数组。
- 在进行排序时,如果 h 很大,我们就能将元素移动到很远的地方,为实现更小的 h 有序创造方便。用这种方式,对于任意以 1 结尾的 h 序列,我们都能将数组排序 → 这就是希尔排序。
5. 代码实现
public class ShellSort {
public static void main(String[] args) {
int[] arr = {9, 6, 11, 3, 5, 12, 8, 7, 10, 15, 14, 4, 1, 13, 2};
print(arr);
sortBySwap(arr);
print(arr);
}
public static void sortBySwap(int[] arr) {
// 比每次按半劈效率高
// Knuth 序列: h = 1, h = 3 * h - 1
int h = 1;
// 退出时 h 等于 arr.length 分割时的最大间隔
while (h <= arr.length / 3) h = h * 3 + 1;
/*
* i = gap;
* - 相隔 gap 个的元素构成一个数组,对这些 [子数组(h数组)] 进行插入排序
* - 回忆插入排序的思想,外层 for 循环为啥 i 上来就是 1?
* 把 n 个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只
* 包含 1 个元素,无序表中包含有 n-1 个元素 (这其中第一个元素在原始
* 数组中的索引不就是 1 吗),排序过程中每次从无序表中取出第一个元素,
* 把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的
* 适当位置,使之成为新的有序表。
* - Shell = 插排思想 + [h数组]
* → i 初始化为 [h数组] 中的无序表的第 1 个元素,也就是 i = gap。
* -----------------------------------------------------------------
* i++;
* - i+=gap 只能实现第 1 个子数组有序,应该把间隔为 gap 的子数组都排序
* - 多个 [h数组] 编制在一起构成原始数组,i++ 直到 length-1,则保证了对
* 每个 [h数组] 的元素都进行了插入排序
* - 外层的一个循环周期(gap次)之后,就会使每个 [h数组] 中的有序表元素数+1
* -----------------------------------------------------------------
* j > gap-1
* - 因为条件的右半部分是 a[j] < a[j-gap]
* 如果条件左边写的是 j > 0,再配合着 a[j-gap] 可不就数组下标越界了吗?
* - 又因为这是 [h数组], 每个 [h数组] 的第1个元素:0, 1, ... , gap-1
* 故结合 a[j-gap],j 最小应等于 gap -> a[j-gap] = a[0]
*/
for (int gap = h; gap > 0; gap = (gap-1)/3)
for (int i = gap; i < arr.length; i++)
for (int j = i; j > gap-1 && arr[j] < arr[j-gap]; j -= gap)
swap(arr, j, j-gap);
}
public static void sortByMove(int[] arr) {
int insertIndex, insertValue;
for (int gap = arr.length >> 1; gap > 0; gap/=2) {
for (int i = gap; i < arr.length; i++) {
insertIndex = i;
insertValue = arr[insertIndex];
while (insertIndex > gap-1 && insertValue < arr[insertIndex-gap]) {
arr[insertIndex] = arr[insertIndex-gap];
insertIndex -= gap;
}
arr[insertIndex] = insertValue;
}
}
}
public static void print(int[] arr) {
for (int i = 0; i < arr.length; i++)
System.out.print(arr[i] + " ");
System.out.println();
}
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}