树_哈夫曼树和哈夫曼编码

(一)哈夫曼树的概念和算法实现

哈夫曼树(最优二叉树):对于给定的一系列带权的叶子结点,带权路径长度(WPL)最短的二叉树

构造一棵哈夫曼树的算法:由n棵树组成的森林一步步合成一棵最优二叉树

1.初始n个叶子结点互相独立,形成n棵树组成的森林

2.做n-1次合并操作:

每次在现有根节点中寻找权值最小的两个结点合并成一棵二叉树,新树的根结点的权值即为原来两个权值之和

在森林中插入新树,删去已合并的两棵老树

3.直到森林中只剩下一棵树,则该树即为所求

由上构造过程,采用数组存储不会带来空间上的浪费而且便于检索。

1 struct node{
2     int weight;
3     int parent;
4     int LChild,RChild;
5 }HTree[MAX_M+10];

构造代码1:每次暴力检索两个最小值

 1 void select(int right,int *c1,int *c2){
 2     int MIN1=INF,MIN2=INF;//最小和次小权值
 3     int MIN1_num=0,MIN2_num=0;//最小和次小结点编号 
 4     for (int i=1;i<=right;i++){
 5         if (HTree[i].parent!=-1) continue;
 6         if (HTree[i].weight<MIN1){
 7             //printf("%d\n",i);
 8             MIN2=MIN1;
 9             MIN2_num=MIN1_num;
10             MIN1=HTree[i].weight;
11             MIN1_num=i;
12         }
13         else if (HTree[i].weight<MIN2){
14             MIN2=HTree[i].weight;
15             MIN2_num=i;
16         }
17     }
18     *c1=MIN1_num;
19     *c2=MIN2_num;
20 }
21 void Create_HTree(int n,int w[]){
22     //初始化
23     int m=2*n-1;//结点总数
24     for (int i=1;i<=m;i++){
25         if (i<=n) HTree[i].weight=w[i];
26         HTree[i].parent=-1;
27         HTree[i].LChild=0;
28         HTree[i].RChild=0;
29     }
30     for (int i=n+1;i<=m;i++){
31         int c1,c2;//选中的两个最值结点编号
32         select(i-1,&c1,&c2);
33         HTree[i].weight=HTree[c1].weight+HTree[c2].weight;
34         HTree[i].LChild=c1;HTree[i].RChild=c2;
35         HTree[c1].parent=i;HTree[c2].parent=i;
36     }
37 }
View Code

构造代码2:用最小堆辅助

//留坑

(二)相关代码

1.求树的WPL值:从叶子结点向上逐层计数

 1 int Calc_wpl(int n){
 2     int ans=0;
 3     for (int i=1;i<=n;i++){
 4         int last_p=HTree[i].parent;
 5         int temp=0;
 6         while (last_p!=-1){
 7             temp++;
 8             last_p=HTree[last_p].parent;
 9         }
10         ans+=temp*HTree[i].weight;
11     }
12     return ans;
13 }
View Code

//

(三)哈夫曼树应用之一:哈夫曼编码

1.对字符串进行编码时需要关注的问题:

(1)编码长度

ASCII码--等长码;

若希望编码总长度尽可能小,一种自然的想法是使出现频率高的字符的编码尽量短,频率低的字符编码适当长一些

(2)避免二义性

前缀码:生成的所有代码串均不是其他串的前缀。这样子的编码系统就具有唯一的对应明文,不需要分隔符

2.哈夫曼编码:对一棵哈夫曼树进行左0右1(或相反)的编码方式,每个叶子均得到从根到它的路径上的01编码,这样得到的即为哈弗曼编码

哈夫曼树的特性保证了哈夫曼编码是最优前缀码。

3.相关题目

ZJU MOOC 05-树9 Huffman Codes 

题目在此

总体思路:一是判断编码长度是否等于哈弗曼编码的长度,二是判断是否前缀码

solution1:偷懒不储存字符不生成哈夫曼码,只用频率当做权值求出wpl(即为哈夫曼码长度),然后O(n^2)暴力判断是否前缀码

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7 #define MAX_N 100
 8 #define MAX_M 2*MAX_N-1
 9 #define INF 70001000
10 struct NODE{
11     int weight;
12     int parent;
13     int LChild,RChild;
14 }HTree[MAX_M+10];
15 void select(int right,int *c1,int *c2);
16 void Create_HTree(int n,int w[]);
17 int Calc_wpl(int n);
18 bool ok_cmp(char s1[],char s2[]){
19     for (int i=0;i<min(strlen(s1),strlen(s2));i++) 
20       if (s1[i]!=s2[i]) return 0;
21     return 1;
22 }
23 bool is_correct(int STD,int n,int num[],char s[MAX_N][MAX_N]){
24     int cnt=0;
25     for (int i=1;i<=n;i++){
26         cnt+=strlen(s[i])*num[i];
27         if (cnt>STD) return 0;
28     }
29     if (cnt!=STD) return 0;
30     //判断是否前缀码
31     for (int i=1;i<n;i++)
32       for (int j=i+1;j<=n;j++)  
33         if (ok_cmp(s[i],s[j])) return 0;
34     return 1;
35 }
36 int main(){
37     int n;
38     int num[MAX_N];
39     scanf("%d",&n);
40     for (int i=1;i<=n;i++){
41         scanf("%*c%*c%d%*c",&num[i]);
42     }
43     Create_HTree(n,num);
44     int standard=Calc(n);
45     int t;
46     scanf("%d\n",&t);
47     while (t--){
48         char sub[MAX_N][MAX_N]={""};
49         for (int i=1;i<=n;i++)
50             scanf("%*c%*c%s",sub[i]);
51         printf("%s\n",is_correct(standard,n,num,sub)?"Yes":"No");
52     }
53     return 0;
54 }
部分代码

solution2:求出haffman code,建树判前缀码  //留坑

猜你喜欢

转载自www.cnblogs.com/hwh-since2019/p/12769124.html