希爾排序是插入排序的改進版,透過「分組插入排序」打破 O(n²) 魔咒,歷史地位極高。本文將深入剖析其核心思想、增量序列選擇對效能的劇烈影響、Java 實作細節、穩定性與適用場景。
h(>1),按間距 h 將陣列分為 h 組。h = h/2 或特定序列),重複上述步驟。關鍵洞見:大增量時元素長距離移動,快速接近最終位置;小增量時陣列近乎有序,插入排序極快。
| 序列名稱 | 公式/數列 | 時間複雜度 | 備註 |
|---|---|---|---|
| Shell 原始 | N/2, N/4, ..., 1 | O(n²) | 最差情況退化 |
| Hibbard | 2ᵏ - 1 (1, 3, 7, 15...) | O(n¹·⁵) | 無公因子,較佳 |
| Sedgewick | 9×4ⁱ - 9×2ⁱ + 1 或 4ⁱ - 3×2ⁱ + 1 | O(n⁴ᐟ³) | 實測表現最優 |
| Knuth (3x+1) | 1, 4, 13, 40... (h=3h+1) | O(n¹·⁵) | 教科書常用,易實作 |
鐵律:相鄰增量不應有除 1 外的公因子,否則前輪排序成果被後輪抵銷。
public class ShellSort {
public static void sort(int[] a) {
int N = a.length;
int h = 1;
while (h < N / 3) h = 3 * h + 1; // 最大增量 < N/3
while (h >= 1) {
// h-有序化
for (int i = h; i < N; i++) {
for (int j = i; j >= h && a[j] < a[j - h]; j -= h) {
swap(a, j, j - h);
}
}
h /= 3; // 下一個增量
}
}
private static void swap(int[] a, int i, int j) {
int t = a[i]; a[i] = a[j]; a[j] = t;
}
}
輸出範例(含中間過程):
5 3 6 2 1 9 4 8 7 1 3 6 2 5 9 4 8 7 1 2 3 6 5 9 4 8 7 1 2 3 5 6 9 4 8 7 1 2 3 4 5 6 9 8 7 1 2 3 4 5 6 8 9 7 1 2 3 4 5 6 7 8 9
希爾排序是演算法史上的里程碑,證明了 O(n²) 非極限。雖已非主流首選,但其「分組預排序」思想啟發了後世無數演算法。掌握增量序列選擇與實作細節,能深化對排序本質的理解。