哈夫曼树
给定一个文本 , 假定是26个英文字符组成的文本 , 根据每个字符出现的次数进行哈夫曼编码,使得编码后的文本占用空间最小。
编码思路:
1:对字母的权值排序 , 从小到大选取2个 , 作为新建父节点的儿子。
2:父节点的值为儿子节点的权值和。
3:把新节点加入集合中。
4:重复上述过程,直到集合为空。
译码过程:
从根节点开始,向叶子节点搜索,规定向左为1,向右为0,到达根节点时的路径就是叶子节点的编码。
代码实现:将每个叶子节点放入set中 , 按照权值排序。
每次取出两个节点,新建父节点,链接。
将新节点加入set中。
重复上述操作直到集合为空or只有一个元素。
1 /*** 2 input : number of every char from a to z from a txt named 2.txt 3 target: build a huffman tree 4 output: every char's code 5 */ 6 #include <bits/stdc++.h> 7 using namespace std; 8 9 class node //node 10 { 11 public: 12 int key; 13 char s; 14 node *left , *right ; 15 }; 16 17 struct comp //sort for set 18 { 19 bool operator()(node *a,node *b) 20 { 21 return a->key < b->key ; 22 } 23 }; 24 int a[30]; 25 multiset<node*,comp>contain; 26 node *root; 27 void init() // initial nodes in set 28 { 29 contain.clear(); 30 for(int i=1;i<=26;i++) 31 { 32 node *ans = new node; 33 ans->left = NULL; 34 ans->right = NULL; 35 ans->key = a[i]; 36 ans->s = i+'a'-1; 37 contain.insert(ans); 38 } 39 } 40 41 node* build() //build a huffman tree and return root 42 { 43 multiset<node*,comp>::iterator iter; 44 while(!contain.empty()) 45 { 46 int i=0; 47 node *p1 , *p2; 48 iter = contain.begin(); 49 p1 = *iter ; 50 contain.erase(iter++); 51 if(iter == contain.end()) return p1; 52 p2 = *iter ; 53 contain.erase(iter); 54 55 node * father = new node; 56 father ->left = p1; 57 father ->right = p2; 58 father ->s = '#'; 59 father ->key = p1->key + p2->key; 60 contain.insert(father); 61 } 62 } 63 64 void preorder(node *x,string fin) //get every char's code 65 { 66 if(x==NULL) return ; 67 if(x->s !='#') cout<<x->s<<" "<<fin<<endl; 68 preorder(x->left,fin+"1"); 69 preorder(x->right,fin+"0"); 70 } 71 int main() 72 { 73 fstream in("2.txt"); 74 for(int i=1;i<=26;i++) 75 in>>a[i]; 76 init(); 77 78 multiset<node*,comp>::iterator iter; 79 for(iter = contain.begin();iter!=contain.end();iter++) 80 { 81 cout<<(*iter)->s<<" " <<(*iter)->key<<" "<<endl; 82 } 83 root = build(); 84 /// build is done 85 /// start to decode 86 preorder(root,""); 87 return 0; 88 }
使用优先队列实现建树过程,用map<char,string>记录各个字符的编码.
1 /// 哈夫曼树的节点 2 class node //node 3 { 4 public: 5 int key; 6 char s; 7 node *left , *right ; 8 }; 9 struct cmp 10 { 11 bool operator() (node* a, node *b) 12 { 13 return a->key > b->key; 14 } 15 }; 16 17 priority_queue <node*,vector<node*>,cmp>contain; 18 node *root; 19 /// 初始化,建立叶子节点 20 void init() // initial nodes in set 21 { 22 for(int i=1;i<=num;i++) 23 { 24 node *ans = new node; 25 ans->left = NULL; 26 ans->right = NULL; 27 ans->key = a[i]; 28 ans->s = b[i]; 29 contain.push(ans); 30 //cout<<ans->s<<" "<<ans->key<<endl; 31 } 32 } 33 34 ///建立哈夫曼树 35 node* build() //build a huffman tree and return root 36 { 37 /// 每次从优先队列中取出两个权值最小的节点,创建一个父节点 38 /// 将父节点加入优先队列 39 /// 重复,知道队列中仅有一个元素,该节点就是哈夫曼树的根节点 40 while(!contain.empty()) 41 { 42 node *p1 , *p2; 43 44 p1 = contain.top(); 45 contain.pop(); 46 if(contain.empty()) return p1; 47 p2 = contain.top() ; 48 contain.pop(); 49 50 node * father = new node; 51 father ->left = p1; 52 father ->right = p2; 53 father ->s = '#'; 54 father ->key = p1->key + p2->key; 55 contain.push(father); 56 } 57 } 58 59 /// 先序遍历哈夫曼树,得到字符的哈夫曼编码 60 void preorder(node *x,string fin) //get every char's code 61 { 62 if(x==NULL) return ; 63 if(x->s !='#') mp[x->s] = fin; 64 preorder(x->left,fin+"1"); 65 preorder(x->right,fin+"0"); 66 }
附: 图形化输出二叉树的方法
思路: 将二叉树逆时针旋转90度,横着输一个出, 使用中序遍历,递归的记录节点的层数以便输出空格,每访问一个节点输出节点和换行
1 void print_tree(node *x,int num) 2 { 3 if(x==NULL) return ; 4 print_tree(x->right,num+1); 5 for(int i=0;i<num;i++) printf("=="); 6 printf("%c ",x->s); 7 print_tree(x->left,num+1); 8 }
最终效果:
凑合着能看出父子关系.没办法,控制台做图形化实在困难,这样的效果已经不错了.