数据结构速通之哈夫曼树
前导概念
1.节点的路径长度:从根节点到该节点路径上经过的节点数
2.树的路径长度:某树所有叶节点的路径长度之和
3.结点的带权路径长度:结点的路径长度与结点权值之积
权值:在实际应用场景中赋予每个节点的属性
4.树的带权路径长度WPL:树的所有叶结点的带权路径长度之和
==哈夫曼树:WPL最小的二叉树==
哈夫曼树的构造
1.给出初始森林,其中每个树均只有一个结点,每个节点均有所对应的权值。
2.循环:合并两棵权值最小的二叉树(引入一个权值为该两棵二叉树权值之和的节点,将这两棵二叉树作为其子树,该树的权值即为根节点的权值),直到森林中只有一棵树。
应用场景:哈夫曼编码
知乎链接
哈夫曼编码是一种对文本存储进行压缩的==不定长编码方式==。
为了解决不定长编码所带来的多义性所创造的最优编码算法。
原理以后再补
实操
相关题目 洛谷
P2168荷马史诗 AcWing
3531
节点类型
1 2 3 4 5
| typedef struct HNode{ int weight; struct HNode *lc; struct HNode *rc; }*Htree;
|
哈夫曼树的构建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| Htree createHtree(int arr[],int n) { Htree forest[N]; Htree root=NULL; int minn,minnSub; for(int i=0;i<n;i++) { Htree temp; temp= (Htree)malloc(sizeof(struct HNode)); temp->lc=temp->rc=NULL; temp->weight=arr[i]; forest[i]=temp; } for(int i=0;i<n-1;i++) { minn=-1,minnSub=-1; for (int j = 0; j < n; j++) { if(forest[j]!=NULL&&minn==-1) { minn=j; continue; } if(forest[j]!=NULL) { minnSub=j; break; } } for(int j=minnSub;j<n;j++) { if(forest[j]!=NULL) { if(forest[j]->weight<forest[minn]->weight) { minnSub=minn; minn=j; } else if(forest[j]->weight<forest[minnSub]->weight) { minnSub=j; } } } Htree root=(Htree)malloc(sizeof(struct HNode)); root->weight=forest[minn]->weight+forest[minnSub]->weight; root->lc=forest[minn]; root->rc=forest[minnSub]; forest[minn]=root; forest[minnSub]=NULL; } return root; }
|
计算哈夫曼树的WPL(递归)
1 2 3 4 5 6 7 8 9 10 11 12
| int getWPL(Htree t,int n){ if(t==NULL){ return 0; } else if(!(t->lc||t->rc)){ return n*t->weight; } else{ return getWPL(t->lc,n+1)+getWPL(t->rc,n+1); } }
|
计算哈夫曼编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void hcode(Htree t,int len,int arr[]){ if(t!=NULL){ if(!(t->lc||t->rc)){ printf("Node:%d:",t->weight); for(int i=0;i<len;i++){ printf("%d",arr[i]); } printf("\n"); } else{ arr[len]=0; hcode(t->lc,len+1,arr); arr[len]=1; hcode(t->rc,len+1,arr); } } }
|