赫夫曼树,即最优二叉树。
给定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;
}