11--霍夫曼樹

[toc]

前言

哈夫曼數是而二叉樹的一種特殊形式,又稱為最優二叉樹,主要用于數據解壓編碼長度的優化.

重要概念

  • 路徑和路徑長度

在一棵樹種,從一個結點往下可以到達孩子或孩子的孩子結點直接的通路,成為路徑.通路分支的數目成為路徑長度.

如果規定,根節點的層數為1,那么到達L層路徑的結點路徑長度為L-1.

75e2faa51c833a4aa5f0225f86b11cc7
  • 結點的權及帶權路徑長度

若將樹中結點賦給一個有著某種含義數值,則這個數值稱為該結點的權。結點的帶權路徑長度為:根結點該結點之間的路徑長度該結點乘積.

98ddf82b2ca10c0d821589302c89cded
e6bd53e9f5058deb3ba391d52e10cb08
  • 樹的帶權路徑長度

樹的帶權路徑長度規定為所有葉子結點的帶權路徑長度之,記為WPL

5ec205642620cf3482f99109dc212afb

圖二叉樹WPL為:

WPL = 5*2+10*2+15*1 = 45

哈夫曼樹

定義

給定n個權值作為n個葉子結點,構造一棵二叉樹,若帶權路徑長度達到最,稱這樣的二叉樹為最優二叉樹,也稱為哈夫曼樹(Huffman Tree).

哈夫曼樹是帶權路徑長度最短的樹,權值較大的結點離根較近.

9b82a5ca0fbf1d8085358996f352e360

葉子結點為A、B、C、D,對應權值分別為7、5、2、4

左邊樹的WPL = 7 * 2 + 5 * 2 + 2 * 2 + 4 * 2 = 36
右邊樹的WPL =7 * 1 + 5 * 2 + 2 * 3 + 4 * 3 = 35

ABCD構成葉子結點的二叉樹形態有許多種,但是WPL最小的樹只有左邊樹所示的形態。則左邊樹霍夫曼樹

構造哈夫曼樹

構造哈夫曼樹主要運用于編碼,稱為哈夫曼編碼.

構造哈夫曼樹的算法如下:

1)對給定的n個權值{W1,W2,W3,...,Wi,...,Wn}構成n棵二叉樹的初始集合F={T1,T2,T3,...,Ti,..., Tn},其中每棵二叉樹Ti中只有一個權值為Wi的根結點,它的左右子樹均為空
2)在F中選取兩棵根結點權值最小的樹作為新構造的二叉樹的左右子樹,新二叉樹的根結點的權值為其左右子樹的根結點的權值之和
3)從F中刪除這兩棵樹,并把這棵新的二叉樹同樣以升序排列加入到集合F中。
4)重復2)和3),直到集合F中只有一棵二叉樹為止

  • 第一步,對結點進行排序


    d59f745a2b88b5642087170045902c61
  • 第二步,將最小的5和8構造傳一棵子樹


    f79a4ebba755b8b7bd3aa592462276ee
  • 第三步,5+8等于13,小于15,將13,15構造成一棵子樹


    28e599a215e8ac6919402f24d843f1c4
  • 第四步,由于13+15>15和27,所以將15,27構造成一棵子樹


    e5291d83746a315b5a4245036fa8eeb5
  • 第五步,30是大于28,30和28構造一棵子樹,


    608fb9885a18a1c0cab82c47516ba66a
  • 最后連起來就是:


    2d0b1ee8b583a77b29c4bf6e23f02dc7
  • 哈夫曼編碼

編碼規則:從根節點出發,向左標記為0,向右標記為1.

3a4e2653ee8d950ec78fd0538e037a78

代碼實現

  • 哈夫曼樹


    8aeb99b546424a41033939ab8c4abfdf
  • 哈夫曼編碼


    24a384a87746fac18b989214c0d9c296
  • 構造結構體

const int MaxValue = `10000`;//初始設定的權值最大值
const int MaxBit = `4`;//初始設定的最大編碼位數
const int MaxN = `10`;//初始設定的最大結點個數

typedef struct HaffNode{
    int weight;
    int flag;
    int parent;
    int leftChild;
    int rightChild;
}HaffNode;

typedef struct Code//存放哈夫曼編碼的數據元素結構
{
    int bit[MaxBit];//數組
    int start;  //編碼的起始下標
    int weight;//字符的權值
}Code;
  • 哈夫曼樹
void Haffman(int weight[],int n,HaffNode *haffTree){
    
    int j,m1,m2,x1,x2;
    
    //1.哈夫曼樹初始化
    //n個葉子結點. 2n-1
    for(int i = 0; i < 2*n-1;i++){
        
        if(I<n)
            haffTree[i].weight = weight[I];
        else
            haffTree[i].weight = 0;
        
        haffTree[i].parent = 0;
        haffTree[i].flag = 0;
        haffTree[i].leftChild = -1;
        haffTree[i].rightChild = -1;
    }
    
    
    //2.構造哈夫曼樹haffTree的n-1個非葉結點
    for (int i = 0; i< n - 1; i++){
         m1 = m2 = MaxValue;
         x1 = x2 = 0;
        //2,4,5,7
        for (j = 0; j< n + i; j++)//循環找出所有權重中,最小的二個值--morgan
        {
            if (haffTree[j].weight < m1 && haffTree[j].flag == 0)
            {
                m2 = m1;
                x2 = x1;
                m1 = haffTree[j].weight;
                x1 = j;
            } else if(haffTree[j].weight<m2 && haffTree[j].flag == 0)
            {
                m2 = haffTree[j].weight;
                x2 = j;
            }
        }
        
        //3.將找出的兩棵權值最小的子樹合并為一棵子樹
        haffTree[x1].parent = n + I;
        haffTree[x2].parent = n + I;
        //將2個結點的flag 標記為1,表示已經加入到哈夫曼樹中
        haffTree[x1].flag = 1;
        haffTree[x2].flag = 1;
        //修改n+i結點的權值
        haffTree[n + i].weight = haffTree[x1].weight + haffTree[x2].weight;
        //修改n+i的左右孩子的值
        haffTree[n + i].leftChild = x1;
        haffTree[n + i].rightChild = x2;
    }
    
}

  • 哈夫曼編碼
void HaffmanCode(HaffNode haffTree[], int n, Code haffCode[])
{
    //1.創建一個結點cd
    Code *cd = (Code * )malloc(sizeof(Code));
    int child, parent;
    //2.求n個葉結點的哈夫曼編碼
    for (int i = 0; i<n; I++)
    {
        //從0開始計數
        cd->start = 0;
        //取得編碼對應權值的字符
        cd->weight = haffTree[i].weight;
        //當葉子結點i 為孩子結點.
        child = I;
        //找到child 的雙親結點;
        parent = haffTree[child].parent;
        //由葉結點向上直到根結點
        while (parent != 0)
        {
            if (haffTree[parent].leftChild == child)
                cd->bit[cd->start] = 0;//左孩子結點編碼0
            else
                cd->bit[cd->start] = 1;//右孩子結點編碼1
            //編碼自增
            cd->start++;
            //當前雙親結點成為孩子結點
            child = parent;
            //找到雙親結點
            parent = haffTree[child].parent;
        }
        
         int temp = 0;

        for (int j = cd->start - 1; j >= 0; j--){
            temp = cd->start-j-1;
            haffCode[i].bit[temp] = cd->bit[j];
        }
      
        //把cd中的數據賦值到haffCode[I]中.
        //保存好haffCode 的起始位以及權值;
        haffCode[i].start = cd->start;
        //保存編碼對應的權值
        haffCode[i].weight = cd->weight;
    }
}

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容