希爾排序 (Shell Sort) 是插入排序的改進版,由 Donald Shell 於 1959 年提出。核心思想:**將陣列按增量分組,對各組進行插入排序,逐步縮小增量至 1**。此時陣列已近乎有序,最終插入排序極快。
d1 > d2 > ... > dt = 1dk,將陣列分為 dk 組 (下標模 dk 相同者同組)為何比直接插入快?
| 序列名稱 | 公式 / 產生方式 | 平均時間複雜度 | 備註 |
|---|---|---|---|
| Shell 原始 | N/2, N/4, ..., 1 | O(N²) | 最差情況退化 |
| Hibbard | 2^k - 1 (1, 3, 7, 15...) | O(N^1.5) | 避免倍數關係 |
| Sedgewick | 9*4^k - 9*2^k + 1 等 | O(N^1.33) | 實務表現優異 |
| Pratt | 2^p * 3^q | O(N log²N) | 常數因子大 |
| Ciura (經驗最佳) | 1, 4, 10, 23, 57, 132, 301, 701... | ~O(N^1.25) | 預計算表,超過時用遞推 |
原則: 最後必為 1;相鄰增量盡量不為倍數關係 (避免重複比較)。
public class ShellSort {
public static void shellSort(int[] arr) {
int n = arr.length;
// 增量序列: n/2, n/4, ..., 1
for (int gap = n >> 1; gap > 0; gap >>= 1) {
// 對每組進行插入排序
for (int i = gap; i < n; i++) {
int temp = arr[i];
int j;
// 組內插入:與 gap 間隔的元素比較
for (j = i; j >= gap && temp < arr[j - gap]; j -= gap) {
arr[j] = arr[j - gap]; // 後移
}
arr[j] = temp; // 插入正確位置
}
}
}
public static void main(String[] args) {
int[] arr = {44, 33, 99, 10, 30, 20, 59, 78, 23, 48};
System.out.print("排序前: ");
printArray(arr);
shellSort(arr);
System.out.print("排序後: ");
printArray(arr);
}
static void printArray(int[] a) {
for (int v : a) System.out.print(v + " ");
System.out.println();
}
}
j -= gap:跨組比較的步長temp 暫存待插入值,避免被覆蓋j >= gap && temp < arr[j-gap] 確保不越界且找到插入點即停止arr[j] = temp 而非 arr[i],因 j 是實際插入位置| 指標 | 數值 |
|---|---|
| 最好時間 | O(N log N) (近乎有序時) |
| 平均時間 | 取決增量序列,O(N^1.25) ~ O(N^1.5) |
| 最壞時間 | O(N²) (Shell 原始序列) |
| 空間複雜度 | O(1) 原地排序 |
| 穩定性 | 不穩定 (跨組交換可能改變相等元素相對順序) |
Arrays.sort 即採用雙軸快排 + 歸併混合)希爾排序是「簡單算法中效能最強」的代表,理解其**分組插入**與**增量序列**兩大核心,不僅能應對面試,更能在資源受限場景下派上用場。