基礎排序算法(六)希爾排序
一 希爾排序
希爾排序是一種非常獨特且高效的排序算法,它通過一種“先宏觀,后微觀”的策略來提升效率
1.1 算法特性
希爾排序特性總結
| 特性 | 說明 |
|---|---|
| 核心思想 | 分而治之:將整個序列按一定間隔(gap)分割成若干子序列,分別進行直接插入排序。然后逐步縮小間隔,直至間隔為1,此時整個序列已基本有序,最后進行一次插入排序即可完成 |
| 時間復雜度 | 取決于增量序列的選擇,通常介于 O(n log2n) 到 O(n2) 之間。使用 Hibbard 或 Knuth 增量序列時,平均復雜度可優化至約 O(n^1.3) 或 O(n^(3/2)) |
| 空間復雜度 | O(1)。是原地排序算法,只需要常數級別的額外空間 |
| 穩定性 | 不穩定。由于元素是跨間隔進行跳躍式移動,可能會改變相同元素的原始相對順序 |
| 主要優勢 | 相比簡單插入排序效率顯著提升;代碼實現相對簡單;是早期突破O(n2)時間復雜度的算法之一 |
| 主要劣勢 | 性能受增量序列選擇影響較大;不穩定;在最壞情況下性能可能不佳 |
1.2 算法原理
希爾排序的巧妙之處在于它通過“增量序列”將大規模無序數據轉化為小規模且基本有序的數據進行排序。其過程可以清晰地分為“分組預排序”和“最終插入排序”兩大階段。下圖以序列 [8, 9, 1, 7, 2, 3, 5, 4, 6, 0]為例,展示了使用初始增量 gap=5進行分組排序,并逐步縮小增量直至為1的全過程。

- 1? 選擇增量序列??:這是希爾排序的“靈魂”。算法首先選擇一個初始間隔(gap),通常為數組長度的一半(如n/2),然后逐步縮小這個間隔(如每次減半),直到間隔變為1。
- ??2 按間隔分組并排序??:
對于每個選定的間隔gap,將數組中所有相距為gap倍數的元素歸為同一組。然后,??分別對這些分組進行直接插入排序??。這一步稱為“預排序”,它使得元素能夠大步地跳躍到其最終位置 附近, 從而大幅減少后續精細排序所需的工作量。 - ??3 縮小間隔重復過程??:完成一次分組排序后,將間隔gap縮小(例如,gap = gap / 2或使用更優的序列如 gap = gap / 3 + 1),然后重復步驟2的分組和排序過程。
- 4?? 最終插入排序??:當間隔gap最終縮小到1時,整個數組已經被“預排序”得基本有序了。此時再對整個數組進行一次標準的直接插入排序。由于序列已基本有序,這次插入排序的效率會非常高,接近O(n)。
1.3 復雜度分析
希爾排序的性能與所選用的??增量序列??密切相關。
1.3.1 時間復雜度
??這是一個復雜的話題,因為沒有一個固定的答案。
使用最簡單的增量序列(如每次減半),最壞情況時間復雜度可能達到 ??O(n2)??。
但使用更優化的增量序列,如??Hibbard增量??(1, 3, 7, ..., 2? - 1)或??Knuth增量??(1, 4, 13, ..., (3? - 1)/2),平均時間復雜度可以優化到約 ??O(n^1.3)?? 或 ??O(n^(3/2))??,這比簡單插入排序的O(n2)要好得多。
需要注意的是,希爾排序的準確平均時間復雜度分析仍然是算法研究中的一個課題。
1.3.2 空間復雜度
??由于希爾排序是原地排序,只需要少量臨時變量(如用于元素交換的temp),因此空間復雜度為 ??O(1)??。
1.4 使用場景
希爾排序在以下場景中表現出色:
- ??中等規模數據排序??:當數據量在數百到數萬之間時,希爾排序通常能提供不錯的性能,且實現比快速排序、歸并排序等更簡單。
- ??內存受限的環境??:由于它是原地排序,空間復雜度為O(1),非常適合嵌入式系統等內存緊張的場景。
- ??作為更復雜算法的子過程??:有時在快速排序等算法的遞歸過程中,當子數組規模較小時,會切換使用希爾排序來提升整體效率。
1.5 代碼實現
1.5.1 C 語言代碼實現
#include <stdio.h>
void shellSort(int arr[], int n) {
// 初始增量gap設為數組長度的一半,隨后逐步縮小增量
for (int gap = n / 2; gap > 0; gap /= 2) {
// 從第gap個元素開始,對每個子序列進行插入排序
for (int i = gap; i < n; i++) {
int temp = arr[i]; // 待插入的元素
int j;
// 對當前子序列進行插入排序:將比temp大的元素后移
for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
arr[j] = arr[j - gap];
}
// 將temp插入到正確位置
arr[j] = temp;
}
}
}
// 打印數組函數
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// 主函數測試
int main() {
int arr[] = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
int n = sizeof(arr) / sizeof(arr[0]);
printf("排序前的數組: \n");
printArray(arr, n);
shellSort(arr, n);
printf("排序后的數組: \n");
printArray(arr, n);
return 0;
}
1.5.2 ?代碼關鍵點解釋
- 外層循環 for (int gap = n / 2; gap > 0; gap /= 2)控制增量的變化。
- 中間層循環 for (int i = gap; i < n; i++)從每個子序列的第二個元素開始遍歷(因為第一個元素可視為已排序)。
- 內層循環 for (j = i; j >= gap && arr[j - gap] > temp; j -= gap)是插入排序的核心,它在當前子序列中為 temp尋找正確的插入位置,并將比它大的元素向后移動。
1.6 常用算法比較
排序算法比較
| 算法 | 平均時間復雜度 | 最壞時間復雜度 | 空間復雜度 | 穩定性 | 主要特點 |
|---|---|---|---|---|---|
| 希爾排序 | 約 O(n^1.3) | O(n2) | O(1) | 不穩定 | 實現簡單,中等規模數據效率高,性能受增量序列影響大 |
| 直接插入排序 | O(n2) | O(n2) | O(1) | 穩定 | 對小規模或基本有序數據效率很高;穩定;簡單 |
| 快速排序 | O(n log n) | O(n2) | O(log n) | 不穩定 | 平均性能極佳,是許多標準庫的實現選擇,但對初始數據敏感 |
| 歸并排序 | O(n log n) | O(n log n) | O(n) | 穩定 | 性能穩定,是穩定的O(n log n)排序,但需要O(n)額外空間 |
| 堆排序 | O(n log n) | O(n log n) | O(1) | 不穩定 | 最壞情況也能保證O(n log n),但常數因子較大,緩存不友好 |
1.7 總結
希爾排序是一種巧妙且實用的排序算法,它通過“分組插入排序”和“逐步細化”的策略,顯著提升了插入排序在處理無序數據時的效率。雖然其性能分析較為復雜,且它不是穩定的排序算法,但其代碼簡潔、空間效率高的特點,使其在特定場景下(如中等規模數據排序、內存受限環境)依然是一個非常有價值的選擇。
posted on 2025-10-31 17:13 weiwei2021 閱讀(1) 評論(0) 收藏 舉報
浙公網安備 33010602011771號