希爾排序
希爾排序是希爾(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
*/