Huffman 樹(最優二叉樹)是帶權路徑長度最短的二叉樹,廣泛用於資料壓縮(Huffman 編碼)。本文提供完整 C 實現:權值輸入 → 樹構建(選最小兩棵合併) → 編碼生成(葉節點向根回溯) → 編碼輸出,採用一維陣列順序存儲,代碼清晰適合教學與嵌入式移植。
typedef struct Node {
int weight; // 權值(葉節點有效,內部節點為子樹權值和)
int parent; // 父節點索引,0 表示根
int lchild, rchild; // 左/右孩子索引,0 表示無(葉節點)
} HTNode, *HuffmanTree; // 結點陣列指針
typedef char **HuffmanCode; // 編碼表:每個字元一個字串
步驟:
1. n 個權值 → n 棵單節點樹(陣列前 n 位)。
2. 重複 n-1 次:從**親為 0** 的節點中選**權值最小的兩個** → 建新節點為父,權值求和 → 存入陣列後續位置。
3. 最後剩一棵樹(索引 2n-2),即 Huffman 樹根。
HuffmanTree create_HuffmanTree(int *weight, int n) {
int total = 2 * n - 1;
HuffmanTree HT = (HuffmanTree)malloc(total * sizeof(HTNode));
if (!HT) exit(-1);
// 初始化葉節點
for (int i = 0; i < n; i++) {
HT[i].weight = weight[i];
HT[i].parent = HT[i].lchild = HT[i].rchild = 0;
}
// 初始化內部節點位置
for (int i = n; i < total; i++) {
HT[i].weight = HT[i].parent = HT[i].lchild = HT[i].rchild = 0;
}
// 構建 n-1 個內部節點
for (int i = n; i < total; i++) {
int min1, min2;
select_minimum(HT, i, &min1, &min2); // 選權值最小且親=0的兩個
HT[min1].parent = i;
HT[min2].parent = i;
HT[i].lchild = min1;
HT[i].rchild = min2;
HT[i].weight = HT[min1].weight + HT[min2].weight;
}
return HT;
}
select_minimum
void select_minimum(HuffmanTree HT, int limit, int *min1, int *min2) {
*min1 = find_min(HT, limit);
*min2 = find_min(HT, limit);
}
int find_min(HuffmanTree HT, int limit) {
int i = 0;
while (HT[i].parent != 0) i++; // 找第一個親=0
int min_idx = i;
int min_w = HT[i].weight;
for (; i < limit; i++) {
if (HT[i].parent == 0 && HT[i].weight < min_w) {
min_w = HT[i].weight;
min_idx = i;
}
}
HT[min_idx].parent = 1; // 標記已選(暫時置 1,區別於真正父節點)
return min_idx;
}
技巧:將「已選」節點 parent 暫設為 1(非 0),下輪選擇時自動跳過,構建完成後真正父節點會覆蓋為正確索引。
void HuffmanCoding(HuffmanTree HT, HuffmanCode *HC, int n) {
*HC = (HuffmanCode)malloc(n * sizeof(char*));
if (!*HC) exit(-1);
char *code = (char*)malloc(n * sizeof(char)); // 臨時緩衝區,最長 n-1 位
if (!code) exit(-1);
code[n-1] = '\0';
for (int i = 0; i < n; i++) {
int cur = i, par = HT[i].parent;
int start = n - 1;
while (par != 0) { // 向根回溯
if (HT[par].lchild == cur) code[--start] = '0';
else code[--start] = '1';
cur = par;
par = HT[par].parent;
}
(*HC)[i] = (char*)malloc((n - start) * sizeof(char));
strcpy((*HC)[i], code + start);
}
free(code);
}
int main() {
int w[] = {5,4,3,2,1};
int n = 5;
HuffmanTree HT = create_HuffmanTree(w, n);
HuffmanCode HC;
HuffmanCoding(HT, &HC, n);
for (int i = 0; i < n; i++)
printf("Weight %d: Code %s\n", w[i], HC[i]);
// 釋放記憶體(略)
return 0;
}
Weight 5: Code 11
Weight 4: Code 10
Weight 3: Code 00
Weight 2: Code 011
Weight 1: Code 010
對應樹結構:權值大的節點(5,4)靠近根(編碼短),權值小的(1,2)較遠(編碼長),符合 Huffman 最優前綴碼性質。
| 階段 | 時間複雜度 | 空間複雜度 | 優化建議 |
|---|---|---|---|
| 選最小 | O(n²) | O(n) | 用最小堆優化至 O(n log n) |
| 構建樹 | O(n) | 同上 | |
| 生成編碼 | O(n²) | 回溯路徑長度和 ≤ n² |