希尔排序
希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n2)的第一批算法之一。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。
希尔排序是把元素按下表的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个数组被分成一组,算法便终止。
算法描述
我们来看下希尔排序的基本步骤,在此我们选择增量 gap = length / 2,缩小增量继续以 gap = gap / 2 的方式,这种增量选择我们可以用一个序列来表示,{n / 2, (n / 2) / 2 ... 1},称为增量序列。希尔排序的增量序列的选择与证明是个数学难题,我们选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。此处我们做示例使用希尔增量。
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:
- 选择一个增量序列 t1,t2,…,tk,其中 ti > tj,tk = 1;
- 按增量序列个数 k,对序列进行 k 趟排序;
- 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
算法分析
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)
- 稳定性:不稳定
代码实现
public class ShellSort {
public static void shellSort(int[] arr) {
// 空数组 或 只有一个元素的数组,则什么都不做。
if (arr == null || arr.length <= 1) return;
// 定义希尔增量。
int gap = arr.length / 2;
// gap缩小到0的时候就退出循环。
while (gap != 0) {
// 每组进行直接插入排序。
for (int i = gap; i < arr.length; i++) { // i 代表待插入元素的索引。
int value = arr[i];
int j = i - gap; // j 代表i的上一个元素,相差一个增量gap。
// j < 0 时退出循环,说明 j 是最小的元素的索引值。
// 或者 arr[j] <= value 时退出循环,说明 j 是比value小的元素的索引值。
for (; j >= 0 && arr[j] > value; j -= gap) {
arr[j + gap] = arr[j]; // 把元素往后挪。
}
arr[j + gap] = value;
}
// 把每一趟排序的结果也输出一下。
print(arr);
// 缩小增量。
gap /= 2;
}
}
public static void main(String[] args) {
int[] arr = {6, 9, 1, 4, 5, 8, 7, 0, 2, 3};
System.out.print("排序前: ");
print(arr);
shellSort(arr);
System.out.print("排序后: ");
print(arr);
}
// 打印数组
public static void print(int[] arr) {
if (arr == null) return;
for(int i : arr) {
System.out.print(i + " ");
}
System.out.println();
}
}
/*
排序前: 6 9 1 4 5 8 7 0 2 3
6 7 0 2 3 8 9 1 4 5
0 1 3 2 4 5 6 7 9 8
0 1 2 3 4 5 6 7 8 9
排序后: 0 1 2 3 4 5 6 7 8 9
*/