1. 程式人生 > >赫夫曼樹的建立

赫夫曼樹的建立

赫夫曼樹,即最優二叉樹。

給定n個權值作為n個葉子結點,構造一棵二叉樹,若該樹的帶權路徑長度達到最小,稱這樣的二叉樹為最優二叉樹,也稱為哈夫曼樹(Huffman Tree)。哈夫曼樹是帶權路徑長度最短的樹,權值較大的結點離根較近。

構造赫夫曼樹:

在這裡插入圖片描述

  1. 把節點的權值按從小到大的順序排列。
  2. 從序列中取出前兩個(最小),作為孩子節點,求出其父節點的權重並加入到序列。
  3. 重複1,2,直到序列中只剩一個節點。這個節點就是赫夫曼樹的根節點。

C++實現:

#include <iostream>
#include <cstring>
using namespace std;
//赫夫曼樹
typedef struct Hfmtree {
    char node_val;
    unsigned int weight;
    Hfmtree *lchild, *rchild;
}*pHfmtree;
//佇列節點(連結串列實現)
typedef struct link_node {
    pHfmtree ptree;
    link_node *next;
}Lnode,*pLnode;


//得到權重
int* get_weight( char *inputString );
//初始化連結串列
void init_link( pLnode &p );
//按照權重,插入連結串列
void insert_into_linklist ( pLnode &head, pLnode val );
//從連結串列中取出節點
pHfmtree remove_from_list( pLnode &head );
//生成赫夫曼樹
pHfmtree createHfmtree( char *inputString, int *weight, pLnode &head );
//釋放記憶體
void clearTree ( pHfmtree head );
//編碼
void encode( pHfmtree root , char *res ,int index );
//解碼
void decode( char *code, pHfmtree root );



int main(int argc, char const *argv[])
{
    char input[50];
    cin>>input;
    //得到輸入字元對應的權重
    int *weight_res = get_weight( input );

    pLnode head;
    init_link( head );
    //cout << "init_link success\n";

    pHfmtree root = createHfmtree( input, weight_res, head );
    //cout << "CreateHfmtree success\n"<<endl;

    char res[20];//存放編碼結果
    //初始化
    res[0] = '0';
    for( int i=1; i<sizeof(res); ++i )
        res[i] = '\0';

    if( NULL==root ){
        cout << "Hfmtree is empty. Fail to encode.\n";
    }
    else{

        cout << "Encode result are:\n";
        encode( root, res , 0 );

        cout << "Decode result are:\n";
        decode( "0101110100110",root );

        clearTree( root );
    }

    delete[] weight_res;
    //cout << "Finally.\n";
    return 0;
}


//得到權重
int* get_weight( char *inputString ) {

    if( !inputString ){
        cout << "input error!\n";
        return NULL;
    }
    int* weight_result = new int[256];
    //初始化權重表
    for( int i=0; i<256; ++i )
        weight_result[i] = 0;

    for( int i=0; inputString[i]!='\0'; ++i ){

        weight_result[ (char)inputString[i] ]++;
    }
    return weight_result;
}

//初始化連結串列
void init_link( pLnode &p ) {

    p = new Lnode;
    p->ptree = NULL;
    p->next = NULL;
}


//按照權重,插入連結串列佇列
void insert_into_linklist ( pLnode &head, pLnode val ){

    pLnode temp = new Lnode;
    temp->ptree = val->ptree;
    temp->next = NULL;

    //如果佇列為空,則直接插在頭節點的後面
    if( NULL==head->next ){
        //cout << "list is empty.\n";
        head->next = temp;
    }
    //否則,根據權重比較,p最終指向
    else{

        pLnode p = head;
        while( NULL!=p->next && p->next->ptree->weight<val->ptree->weight )
            p = p->next;
        temp->next = p->next;
        p->next = temp;
    }
    //cout << "insert one.\n";
}

//從佇列中取出節點
pHfmtree remove_from_list( pLnode &head ){

    if( NULL==head->next ){
        cout << "List is empty! Fail to remove."<<endl;
        return NULL;
    }

    pHfmtree first = head->next->ptree;

    pLnode todel = head->next;
    head->next = todel->next;
    delete todel;

    //cout << "remove ing\n";
    return first;
}

//生成赫夫曼樹
pHfmtree createHfmtree( char *inputString, int *weight_res, pLnode &head ){

    //生成連結串列
    for( int i=0; i<256; ++i ) {

        if( weight_res[ i ]>0 ) {
            pHfmtree tempTree = new Hfmtree;
            tempTree->node_val = (char)i;
            tempTree->weight = weight_res[ i ];

            //cout << (char)i<<"    "<<weight_res[i]<<endl;

            pLnode tempNode = new Lnode;
            tempNode->ptree = tempTree;

            insert_into_linklist( head, tempNode );
        }
    }
    //cout << "insert success...."<<endl;

    pHfmtree  node_1,node_2, sum_node;
    //當佇列中還有至少2個節點時,繼續取出,直到只剩一個節點
    while( NULL!=head->next->next ) {

        //每次取出前兩個(權重最小),並插入一個新的節點(權重是這兩個的權重之和)
        node_1 = remove_from_list( head );
        node_2 = remove_from_list( head );
        sum_node = new Hfmtree;
        sum_node->weight = node_1->weight + node_2->weight;

        sum_node->lchild = node_1;
        sum_node->rchild = node_2;

        pLnode sum_link_node = new Lnode;
        sum_link_node->ptree = sum_node;
        insert_into_linklist( head, sum_link_node );
    }

    //最後,佇列中只剩頭結點和赫夫曼樹的根節點,將其釋放,這個佇列也就清理了。
    delete head->next;
    delete head;

    return head->next->ptree;//返回赫夫曼樹的根
}

//清理赫夫曼樹
void clearTree ( pHfmtree head ) {

    if( NULL==head )
        return;
    if( NULL!=head->lchild )
        clearTree( head->lchild );
    if( NULL!=head->rchild )
        clearTree( head->rchild );
    delete head;
}

//編碼
void encode( pHfmtree node , char *res, int index ){

    if( NULL==node->lchild&&NULL==node->rchild ){
        cout<< node->node_val <<" :   "<< res <<endl;
        return;
    }

    pHfmtree left = node->lchild;
    if( NULL!=left ){
        res[index] = '0';
        encode( left, res,index+1 );
        res[index] = '\0';
    }


    pHfmtree right = node->rchild;
    if( NULL!=right ){
        res[index] = '1';
        encode( right, res , index+1 );
        res[index] = '\0';
    }
}

//解碼
void decode( char *code, pHfmtree root ){

    //如果赫夫曼樹只有一個跟節點
    if( NULL==root->lchild&&NULL==root->rchild ) {
        for( int i=0; i<strlen(code); ++i ) {
            cout << root->node_val;
        }
        cout << endl;
        return;
    }
    //有多個節點
    pHfmtree p = root;
    for( int i=0; i<strlen(code); ++i ){

        if( code[i]=='0' )
            p = p->lchild;
        if( NULL==p->lchild&&NULL==p->rchild ) {

            cout << p->node_val;
            p = root;
            continue;
        }

        if( code[i]=='1' )
            p = p->rchild;
        if( NULL==p->lchild&&NULL==p->rchild ) {

            cout << p->node_val;
            p = root;
            continue;
        }
    }
    cout << endl;
}

執行結果:

在這裡插入圖片描述