赫夫曼樹的建立
阿新 • • 發佈:2018-12-18
赫夫曼樹,即最優二叉樹。
給定n個權值作為n個葉子結點,構造一棵二叉樹,若該樹的帶權路徑長度達到最小,稱這樣的二叉樹為最優二叉樹,也稱為哈夫曼樹(Huffman Tree)。哈夫曼樹是帶權路徑長度最短的樹,權值較大的結點離根較近。
構造赫夫曼樹:
- 把節點的權值按從小到大的順序排列。
- 從序列中取出前兩個(最小),作為孩子節點,求出其父節點的權重並加入到序列。
- 重複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; }