當數據量遠超記憶體容量(如 512MB 甚至 1GB 整數排序),內部排序算法失效,必須轉向外部排序。本文以完整 C 實現為例,深度講解「分治+多路歸並」核心流程、敗者樹優化原理、測試數據生成及跨平台編譯運行指南。
假設 72KB 文件(18K 整數),磁盤塊 4KB:
磁盤 I/O 次數 ∝ logK(M),增大 K 可減少輪數,但會增加單輪合併複雜度。
傳統 K 路合併需比較 K-1 次選出最小值,總複雜度 O(N·K)。敗者樹(Loser Tree)是完全二叉樹結構,葉節點存 K 路當前元素,非葉節點存「較大者」索引,根節點即全局最小值索引。調整複雜度 O(log K),總複雜度降為 O(N·log K),對海量數據極具價值。
構建流程:自底向上比較,敗者上移,勝者繼續向上 PK。調整時僅需從葉節點向根路徑比較 log K 次。
typedef struct run_t {
int *buf; // 輸入緩衝區
int length; // 緩衝區有效元素數
int offset; // 文件讀取位置
int idx; // 緩衝區讀指針
} run_t;
static int ls[MAX_WAYS]; // 敗者樹數組,ls[0] 存最小元素所在路索引
# 生成 512MB 隨機二進制文件 (128M 整數)
dd if=/dev/urandom of=random.dat bs=1M count=512
編譯運行:
gcc sort.c -o sort -g
./sort random.dat 64 # 64 路歸並
實測數據 (i5/4GB RAM):
| 文件大小 | 耗時 |
|---|---|
| 128M | 14.72 秒 |
| 256M | 30.89 秒 |
| 512M | 71.65 秒 |
| 1G | 169.18 秒 |
近線性擴展,驗證敗者樹 + 多路歸並高效性。
附贈簡易查看程序,每行打印 10 個整數,便於人工抽查或管道對比 `sort -c`。
外部排序是大數據處理基石,掌握其工程化實現,讓你從容應對「內存裝不下」的排序挑戰。