---
# System prepended metadata

title: 霍夫曼編碼（Huffman Encoding）

---

# 霍夫曼編碼（Huffman Encoding）
:::spoiler
1. 這是貪婪演算法的經典問題，已知每個字元的出現頻率，經由霍夫曼編碼可以求得最短的編碼結果，霍夫曼編碼使用可以變動長度的$0$與$1$數字進行編碼。
2. 本題融合之後的樣板函式庫（STL）、樹狀結構與深度優先搜尋章節，可以先瞭解概念部分，若程式看不懂可以先跳過，等熟悉樣板函式庫（STL）、樹狀結構與深度優先搜尋等概念後，再回來複習程式碼。
:::

### 輸入說明
每次輸入數字$n$，$n$表示字元個數，輸入$n$小於$26$，之後有$n$ 行分別是每一行為一個小寫英文字母與一個整數組成，小寫英文字母表示被編碼的字元，而整數表示出現的頻率，數值越大表示頻率越高。

### 輸出說明
輸出每個小寫英文字母的編碼。

### 輸入輸出範例
輸入範例
5
a 10
b 4
c 5
d 7
e 8

輸出範例
d 00
e 01
b 100
c 101
a 11

### 解題構思
1. 貪婪準則是先將所有字元依照出現頻率的由小到大進行排序。
2. 優先考慮出現頻率最低的兩個字元，組合成新的節點，此節點的頻率為兩個目前最小字元頻率的加總，將此節點重新加入所有字元的排序。
3. 再取出最小的兩個字元或節點，組合成新的節點，此節點的頻率為兩個目前最小字元頻率的加總，將此節點重新加入所有字元的排序。
4. 如此不斷重複，直到最後剩下一個節點。
5. 編碼為最上層的左邊編碼$0$而右邊編碼$1$，從上到下重複左邊編碼$0$而右邊編碼$1$，直到無法下去為止，越下面字元編碼越長。

```cpp=
#include <iostream>
#include <algorithm>       // for sort()
#include <deque>         // for deque object, front(), pop_front()

using namespace std;

const int MAXN = 100+5;//最大字元數量

//自訂huffman tree 的節點(node)的資料型態
typedef struct _node{
    int id;             // node 編碼
    char ch;            // node 字元
    int weight;         // node 權重
    bool isSrc;         // node 是否為原始節點
    int left;           // node 的左邊的node編號
    int right;          // node 的右邊的node編號
}NODE;
NODE nodes[MAXN]; // 儲存輸入node的陣列

deque<NODE> myQue; //儲存 huttman tree node資料
    

int main(){
    /* 輸入&排序 */
    int n;  // 字元個數
    tmp.clear();    // 請空tmp
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>nodes[i].ch>>nodes[i].fre;
        nodes[i].id=i;
        nodes[i].isLeaf=1;
        tmp.push_back(nodes[i]);
    }
    sort(tmp.begin(),tmp.end(),cmp);

    /* 處理 */
    int newID=n;    // 新NODE的ID從n開始
    while(tmp.size()>1){
        NODE x=tmp.front();  // tmp中fre最小元素
        tmp.pop_front();
        NODE y=tmp.front();  // tmp中fre次小元素
        tmp.pop_front();

        NODE z;
        z.id=newID;
        z.fre=x.fre+y.fre;  // 兩fre相加
        z.isLeaf=0;
        z.lefID=x.id;
        z.rigID=y.id;
        nodes[newID]=z; //將x+y加入nodes
        tmp.push_back(z);   // 將x+y加入tmp
        sort(tmp.begin(),tmp.end(),cmp);
        newID++;
    }

    dfs(tmp.front().id,0);
}

bool cmp(NODE p,NODE q){
    return p.fre<q.fre; // 按fre(頻率)由小到大排列
}
char res[20];

void dfs(int topID,int level){
    if(!nodes[topID].isLeaf){
        res[level]='0';
        dfs(nodes[topID].lefID,level+1);
        res[level]='1';
        dfs(nodes[topID].rigID,level+1);
    }else{
        cout<<nodes[topID].ch<<" ";
        for(int i=0;i<level;i++) cout<<res[i];
        cout<<"\n";
    }
}
```