數(shù)據(jù)結(jié)構(gòu)(哈夫曼樹)

1. 哈夫曼樹的基本概念

哈夫曼樹又稱最優(yōu)樹烫映,是一類帶權(quán)路徑長(zhǎng)度最短的樹。

路徑:從樹中一個(gè)結(jié)點(diǎn)到另一個(gè)結(jié)點(diǎn)之間的分支構(gòu)成這兩個(gè)結(jié)點(diǎn)之間的路徑害晦。
路徑長(zhǎng)度:路徑上的分支數(shù)目稱作路徑長(zhǎng)度螃宙。
樹的路徑長(zhǎng)度:從樹根到每一結(jié)點(diǎn)的路徑之和慨仿。
權(quán):賦予某個(gè)實(shí)體的一個(gè)量,是對(duì)實(shí)體的某個(gè)或某些屬性的數(shù)值描述晒骇。在數(shù)據(jù)結(jié)構(gòu)中霉撵,實(shí)體有結(jié)點(diǎn)(元素)和邊(關(guān)系)兩大類磺浙,所以對(duì)應(yīng)有結(jié)點(diǎn)權(quán)和邊權(quán)。
結(jié)點(diǎn)的帶權(quán)路徑長(zhǎng)度:從該結(jié)點(diǎn)到樹根之間的路徑長(zhǎng)度與結(jié)點(diǎn)上權(quán)的乘積徒坡。
樹的帶權(quán)路徑長(zhǎng)度:樹中所有葉子結(jié)點(diǎn)的帶權(quán)路徑長(zhǎng)只和撕氧。
哈夫曼樹:假設(shè)有m個(gè)權(quán)值{w1,w2,w3,...,wn},可以構(gòu)造一棵含有n個(gè)葉子結(jié)點(diǎn)的二叉樹喇完,每個(gè)葉子結(jié)點(diǎn)的權(quán)為wi伦泥,則其中帶權(quán)路徑長(zhǎng)度WPL最小的二叉樹稱做最優(yōu)二叉樹或哈夫曼樹。


2.哈夫曼樹的構(gòu)造算法

(1) 哈夫曼樹的構(gòu)造過(guò)程

(2) 哈夫曼樹的實(shí)現(xiàn)

由于哈夫曼樹中沒(méi)有度為1的結(jié)點(diǎn)锦溪,則一棵有n個(gè)葉子結(jié)點(diǎn)的哈夫曼樹共有2n -1個(gè)結(jié)點(diǎn)不脯,可以存儲(chǔ)在一個(gè)大小為2n-1的一維數(shù)組中。
weight parent lchild rchild

//哈夫曼樹的存儲(chǔ)表示
typedef struct{
    int weight; //結(jié)點(diǎn)的權(quán)重
    int parent,lchild,rchild;//結(jié)點(diǎn)的雙親刻诊、左孩子防楷、右孩子的下標(biāo)
}HTNode,*HuffmanTree

哈夫曼樹的各結(jié)點(diǎn)存儲(chǔ)由HuffmanTree定義的動(dòng)態(tài)分配的數(shù)組中,為了實(shí)現(xiàn)方便數(shù)組的0號(hào)單元不使用则涯,從1號(hào)單元開(kāi)始使用复局,所以數(shù)組的大小為2n。將葉子結(jié)點(diǎn)集中存儲(chǔ)在前面部分1~n個(gè)位置粟判,而后n-1個(gè)位置存儲(chǔ)其余非葉子結(jié)點(diǎn)亿昏。

  1. 初始化:首先動(dòng)態(tài)申請(qǐng)2n個(gè)單元,然后循環(huán)2n-1次档礁,從1號(hào)單元開(kāi)始龙优,依次將1至2n-1所有單元中的雙親、左孩子事秀、右孩子的下標(biāo)初始化為0彤断,最后再循環(huán)n次,輸入前n個(gè)單元中葉子的權(quán)值易迹。
  2. 創(chuàng)建樹:循環(huán)n-1次宰衙,通過(guò)n-1次選擇、刪除與合并來(lái)創(chuàng)建哈夫曼樹睹欲。選擇是從當(dāng)前森林中選擇雙親為0且權(quán)值最小的兩個(gè)樹根結(jié)點(diǎn)s1和s2供炼。刪除是指將結(jié)點(diǎn)s1和s2的雙親改為非0。合并就是將s1和s2的權(quán)值和作為一個(gè)新結(jié)點(diǎn)的權(quán)值依次存入到數(shù)組的第n+1之后的單元中窘疮,同時(shí)紀(jì)錄這個(gè)新結(jié)點(diǎn)左孩子的下標(biāo)s1袋哼,右孩子的下標(biāo)s2。
// 選擇權(quán)值最小的兩顆樹 
void SelectMin(HuffmanTree hT, int n, int &s1, int &s2)
{
    s1 = s2 = 0;

    int i;
    for(i = 1; i < n; ++ i){
        if(0 == hT[i].parent){
            if(0 == s1){
                s1 = i;
            }
            else{
                s2 = i;
                break;
            }
        }
    }
    if(hT[s1].weight > hT[s2].weight){
        int t = s1;
        s1 = s2;
        s2 = t;
    }

    for(i += 1; i < n; ++ i){
        if(0 == hT[i].parent){
            if(hT[i].weight < hT[s1].weight){
                s2 = s1;
                s1 = i;
            }else if(hT[i].weight < hT[s2].weight){
                s2 = i;
            }
        }
    }
}
// 構(gòu)造有n個(gè)權(quán)值(葉子節(jié)點(diǎn))的哈夫曼樹 
void CreateHufmanTree(HuffmanTree &hT)
{
    int n, m;
    cin >> n;
    m = 2*n - 1;

    hT = new HTNode[m + 1];    // 0號(hào)節(jié)點(diǎn)不使用 
    for(int i = 1; i <= m; ++ i){
        hT[i].parent = hT[i].lChild = hT[i].rChild = 0;
    }
    for(int i = 1; i <= n; ++ i){
        cin >> hT[i].weight;    // 輸入前n個(gè)單元中葉子結(jié)點(diǎn)的權(quán)值 
    }
    hT[0].weight = m;    // 用0號(hào)節(jié)點(diǎn)保存節(jié)點(diǎn)數(shù)量 

    /****** 初始化完畢, 創(chuàng)建哈夫曼樹 ******/
    for(int i = n + 1; i <= m; ++ i){
          //通過(guò)n-1次的選擇闸衫、刪除涛贯、合并來(lái)創(chuàng)建二叉樹
        int s1, s2;
        SelectMin(hT, i, s1, s2);

        hT[s1].parent = hT[s2].parent = i;
        hT[i].lChild = s1; hT[i].rChild = s2;    // 作為新節(jié)點(diǎn)的孩子 
        hT[i].weight = hT[s1].weight + hT[s2].weight;    // 新節(jié)點(diǎn)為左右孩子節(jié)點(diǎn)權(quán)值之和 
    }
}

C++ 完整代碼

#include <iostream>
#include <iomanip>
using namespace std;

//哈夫曼樹的存儲(chǔ)表示
typedef struct
{
    int weight;    // 權(quán)值
    int parent, lChild, rChild;    // 雙親及左右孩子的下標(biāo) 
}HTNode, *HuffmanTree;


// 選擇權(quán)值最小的兩顆樹 
void SelectMin(HuffmanTree hT, int n, int &s1, int &s2)
{
    s1 = s2 = 0;

    int i;
    for(i = 1; i < n; ++ i){
        if(0 == hT[i].parent){
            if(0 == s1){
                s1 = i;
            }
            else{
                s2 = i;
                break;
            }
        }
    }
    if(hT[s1].weight > hT[s2].weight){
        int t = s1;
        s1 = s2;
        s2 = t;
    }

    for(i += 1; i < n; ++ i){
        if(0 == hT[i].parent){
            if(hT[i].weight < hT[s1].weight){
                s2 = s1;
                s1 = i;
            }else if(hT[i].weight < hT[s2].weight){
                s2 = i;
            }
        }
    }
}

// 構(gòu)造有n個(gè)權(quán)值(葉子節(jié)點(diǎn))的哈夫曼樹 
void CreateHufmanTree(HuffmanTree &hT)
{
    int n, m;
    cin >> n;
    m = 2*n - 1;

    hT = new HTNode[m + 1];    // 0號(hào)節(jié)點(diǎn)不使用 
    for(int i = 1; i <= m; ++ i){
        hT[i].parent = hT[i].lChild = hT[i].rChild = 0;
    }
    for(int i = 1; i <= n; ++ i){
        cin >> hT[i].weight;    // 輸入權(quán)值 
    }
    hT[0].weight = m;    // 用0號(hào)節(jié)點(diǎn)保存節(jié)點(diǎn)數(shù)量 

    /****** 初始化完畢, 創(chuàng)建哈夫曼樹 ******/
    for(int i = n + 1; i <= m; ++ i){
        int s1, s2;
        SelectMin(hT, i, s1, s2);

        hT[s1].parent = hT[s2].parent = i;
        hT[i].lChild = s1; hT[i].rChild = s2;    // 作為新節(jié)點(diǎn)的孩子 
        hT[i].weight = hT[s1].weight + hT[s2].weight;    // 新節(jié)點(diǎn)為左右孩子節(jié)點(diǎn)權(quán)值之和 
    }
}

int HuffmanTreeWPL_(HuffmanTree hT, int i, int deepth)
{
    if(hT[i].lChild == 0 && hT[i].rChild == 0){
        return hT[i].weight * deepth;
    }
    else{
        return HuffmanTreeWPL_(hT, hT[i].lChild, deepth + 1) + HuffmanTreeWPL_(hT, hT[i].rChild, deepth + 1);
    }
}

// 計(jì)算WPL(帶權(quán)路徑長(zhǎng)度) 
int HuffmanTreeWPL(HuffmanTree hT)
{
    return HuffmanTreeWPL_(hT, hT[0].weight, 0);
}

// 輸出哈夫曼樹各節(jié)點(diǎn)的狀態(tài) 
void Print(HuffmanTree hT)
{
    cout << "index weight parent lChild rChild" << endl;
    cout << left;    // 左對(duì)齊輸出 
    for(int i = 1, m = hT[0].weight; i <= m; ++ i){
        cout << setw(5) << i << " ";
        cout << setw(6) << hT[i].weight << " ";
        cout << setw(6) << hT[i].parent << " ";
        cout << setw(6) << hT[i].lChild << " ";
        cout << setw(6) << hT[i].rChild << endl;
    }
}

// 銷毀哈夫曼樹 
void DestoryHuffmanTree(HuffmanTree &hT)
{
    delete[] hT;
    hT = NULL;
}

int main()
{
    HuffmanTree hT;
    CreateHufmanTree(hT);
    Print(hT); 
    cout << "WPL = " << HuffmanTreeWPL(hT) << endl;
    DestoryHuffmanTree(hT);
    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蔚出,隨后出現(xiàn)的幾起案子弟翘,更是在濱河造成了極大的恐慌虫腋,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稀余,死亡現(xiàn)場(chǎng)離奇詭異悦冀,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)睛琳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門盒蟆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人师骗,你說(shuō)我怎么就攤上這事历等。” “怎么了丧凤?”我有些...
    開(kāi)封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵募闲,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我愿待,道長(zhǎng)浩螺,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任仍侥,我火速辦了婚禮要出,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘农渊。我一直安慰自己患蹂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布砸紊。 她就那樣靜靜地躺著传于,像睡著了一般。 火紅的嫁衣襯著肌膚如雪醉顽。 梳的紋絲不亂的頭發(fā)上沼溜,一...
    開(kāi)封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音游添,去河邊找鬼系草。 笑死,一個(gè)胖子當(dāng)著我的面吹牛唆涝,可吹牛的內(nèi)容都是我干的找都。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼廊酣,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼能耻!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤嚎京,失蹤者是張志新(化名)和其女友劉穎嗡贺,沒(méi)想到半個(gè)月后隐解,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鞍帝,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年煞茫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帕涌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡续徽,死狀恐怖蚓曼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钦扭,我是刑警寧澤纫版,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站客情,受9級(jí)特大地震影響其弊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜膀斋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一梭伐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧仰担,春花似錦糊识、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至贮尉,卻和暖如春拌滋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绘盟。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工鸠真, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人龄毡。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓吠卷,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親沦零。 傳聞我的和親對(duì)象是個(gè)殘疾皇子祭隔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 一、 一些基本概念1、 路徑長(zhǎng)度樹中一個(gè)結(jié)點(diǎn)到另一個(gè)結(jié)點(diǎn)之間的分支構(gòu)成這兩個(gè)結(jié)點(diǎn)之間的路徑疾渴,路徑上的分支數(shù)目為...
    Qi0907閱讀 2,585評(píng)論 0 1
  • 這篇文章收錄在我的 Github 上 algorithms-tutorial千贯,另外記錄了些算法題解,感興趣的可以看...
    Lindz閱讀 8,814評(píng)論 1 6
  • 你比他好一點(diǎn)搞坝,他不會(huì)承認(rèn)你搔谴,反而會(huì)嫉妒你,只有你比他好很多桩撮,他才會(huì)承認(rèn)你敦第,然后還會(huì)很崇拜你,所以要做店量,就一定要比別...
    神經(jīng)騷棟閱讀 2,670評(píng)論 5 13
  • 五蓮縣實(shí)驗(yàn)小學(xué)2013級(jí)10班侯林汝 讀了《水滸傳》芜果,一百零八個(gè)英雄好漢在我的心目中,不斷高大起來(lái)…… 《水滸...
    侯林襖閱讀 251評(píng)論 0 0
  • 代碼1非felx:http://js.jirengu.com/dowum/13代碼1flex:http://js....
    upup_dayday閱讀 213評(píng)論 0 0