最优二叉树——哈夫曼树
一:什么是最优二叉树?
从我个人理解来说,最优二叉树就是从已给出的目标带权结点(单独的结点) 经过一种方式的组合形成一棵树.使树的权值最小. 最优二叉树是带权路径长度最短的二叉树。根据结点的个数,权值的不同,最优二叉树的形状也各不相同。它们的共同点是:带权值的结点都是叶子结点。权值越小的结点,其到根结点的路径越长
官方定义:
在权为wl,w2,…,wn的n个叶子所构成的所有二叉树中,带权路径长度最小(即代价最小)的二叉树称为最优二叉树或哈夫曼树。
二:下面先弄清几个几个概念:
1.路径长度
在树中从一个结点到另一个结点所经历的分支构成了这两个结点间的路径上的分支数称为它的路径长度
2.树的路径长度
树的路径长度是从树根到树中每一结点的路径长度之和。在结点数目相同的二叉树中,完全二叉树的路径长度最短。
3.树的带权路径长度(Weighted Path Length of Tree,简记为WPL)
结点的权:在一些应用中,赋予树中结点的一个有某种意义的实数。
结点的带权路径长度:结点到树根之间的路径长度与该结点上权的乘积。
树的带权路径长度(Weighted Path Length of Tree):定义为树中所有叶结点的带权路径长度之和,通常记为:
其中:
n表示叶子结点的数目
wi和li分别表示叶结点ki的权值和根到结点ki之间的路径长度。
树的带权路径长度亦称为树的代价。
三:用一个例子来理解一下以上概念
【例】给定4个叶子结点a,b,c和d,分别带权7,5,2和4。构造如下图所示的三棵二叉树(还有许多棵),它们的带权路径长度分别为:
(a)WPL=7*2+5*2+2*2+4*2=36
(b)WPL=7*3+5*3+2*1+4*2=46
(c)WPL=7*1+5*2+2*3+4*3=35
其中(c)树的WPL最小,可以验证,它就是哈夫曼树。
注意:
① 叶子上的权值均相同时,完全二叉树一定是最优二叉树,否则完全二叉树不一定是最优二叉树。
② 最优二叉树中,权越大的叶子离根越近。
③ 最优二叉树的形态不唯一,WPL最小
四.哈夫曼算法
对于给定的叶子数目及其权值构造最优二叉树的方法,由于这个算法是哈夫曼提出来的,故称其为哈夫曼算法。其基本思想是:
(1)根据给定的n个权值wl,w2,…,wn构成n棵二叉树的森林F={T1,T2,…,Tn},其中每棵二叉树Ti中都只有一个权值为wi的根结点,其左右子树均空。
(2)在森林F中选出两棵根结点权值最小的树(当这样的树不止两棵树时,可以从中任选两棵),将这两棵树合并成一棵新树,为了保证新树仍是二叉树,需 要增加一个新结点作为新树的根,并将所选的两棵树的根分别作为新根的左右孩子(谁左,谁右无关紧要),将这两个孩子的权值之和作为新树根的权值。
(3)对新的森林F重复(2),直到森林F中只剩下一棵树为止。这棵树便是哈夫曼树。
注意:
① 初始森林中的n棵二叉树,每棵树有一个孤立的结点,它们既是根,又是叶子
② n个叶子的哈夫曼树要经过n-1次合并,产生n-1个新结点。最终求得的哈夫曼树中共有2n-1个结点。
③ 哈夫曼树是严格的二叉树,没有度数为1的分支结点。
五:最优二叉树算法具体实现思路
在构造哈夫曼树时,可以设置一个结构数组HuffNode保存哈夫曼树中各结点的信息,根据二叉树的性质可知,具有n个叶子结点的哈夫曼树共有2n-1个结点,所以数组HuffNode的大小设置为2n-1,数组元素的结构形式如下:
weight |
lchild |
rchild |
parent |
其中,weight域保存结点的权值,lchild和rchild域分别保存该结点的左、右孩子结点在数组HuffNode中的序号,从而建立起结 点之间的关系。为了判定一个结点是否已加入到要建立的哈夫曼树中,可通过parent域的值来确定。初始时parent的值为-1,当结点加入到树中时, 该结点parent的值为其双亲结点在数组HuffNode中的序号,就不会是-1了。构造哈夫曼树时,首先将由n个字符形成的n个叶结点存放到数组HuffNode的前n个分量中,然后根据前面介绍的哈夫曼方法的基本思想,不断将两个小子树合并为一个较大的子树,每次构成的新子树的根结点顺序放到HuffNode数组中的前n个分量的后面。
具体实现:
1)存储结构
#define n 100 //叶结点数目 #define m 2*n-1 //树中结点总数 typedef struct { floatweight; //权值,设权值均大于零 intlchild,rchild,parent; //左右孩子及双亲指针 } HTNode; typedef HTNode HuffmanTree[m]; //哈夫曼树是一维数组
(2)赫夫曼算法的数组法构造
void CreateHuffmanTree(HuffmanTree T) { int i,p1,p2; //构造哈夫曼树,T[m-1]为其根结点 InitHuffmanTree(T); //T初始化 InputWeight(T); //输入叶子权值至T[0..n-1]的weight域 for(i=n;i<m;i++) { SelectMin(T,i-1,&p1,&p2);//共进行n-1次合并,新结点依次存于T[i]中 //在T[0…i-1]中选择两个权最小的根结点,其序号分别为p1和p2 T[p1].parent=T[p2].parent=i; T[i].1child=p1; //最小权的根结点是新结点的左孩子 T[i].rchild=p2; //次小权的根结点是新结点的右孩子 T[i].weight=T[p1].weight+T[p2].weight; }//for }//CreateHuffman
C语言实现全部代码:
#include "stdio.h" #include "stdlib.h" #define m 100 struct ptree //定义二叉树结点类型 { int w; //定义结点权值 struct ptree *lchild; //定义左子结点指针 struct ptree *rchild; //定义右子结点指针 }; struct pforest //定义链表结点类型 { struct pforest *link; struct ptree *root; }; int WPL=0; //初始化WTL为0 struct ptree *hafm(); void travel(); struct pforest *inforest(struct pforest*f,struct ptree *t); void travel(struct ptree *head,int n) { //为验证harfm算法的正确性进行的遍历 struct ptree *p; p=head; if(p!=NULL) { if((p->lchild)==NULL && (p->rchild)==NULL) //如果是叶子结点 { printf("%d ",p->w); printf("the hops of the node is: %d/n",n); WPL=WPL+n*(p->w); //计算权值 }//if travel(p->lchild,n+1); travel(p->rchild,n+1); }//if }//travel struct ptree *hafm(int n, int w[m]) { struct pforest *p1,*p2,*f; struct ptree *ti,*t,*t1,*t2; int i; f=(pforest *)malloc(sizeof(pforest)); f->link=NULL; for(i=1;i<=n;i++) //产生n棵只有根结点的二叉树 { ti=(ptree*)malloc(sizeof(ptree));//开辟新的结点空间 ti->w=w[i]; //给结点赋权值 ti->lchild=NULL; ti->rchild=NULL; f=inforest(f, ti); //按权值从小到大的顺序将结点从上到下地挂在一颗树上 }//for while(((f->link)->link)!=NULL)//至少有二棵二叉树 { p1=f->link; p2=p1->link; f->link=p2->link; //取出前两棵树 t1=p1->root; t2=p2->root; free(p1); //释放p1 free(p2); //释放p2 t=(ptree *)malloc(sizeof(ptree));//开辟新的结点空间 t->w = (t1->w)+(t2->w); //权相加 t->lchild=t1; t->rchild=t2; //产生新二叉树 f=inforest(f,t); }//while p1=f->link; t=p1->root; free(f); return(t); //返回t } pforest *inforest(struct pforest *f,structptree *t) { //按权值从小到大的顺序将结点从上到下地挂在一颗树上 struct pforest *p, *q, *r; struct ptree *ti; r=(pforest *)malloc(sizeof(pforest)); //开辟新的结点空间 r->root=t; q=f; p=f->link; while (p!=NULL) //寻找插入位置 { ti=p->root; if(t->w > ti->w) //如果t的权值大于ti的权值 { q=p; p=p->link; //p向后寻找 }//if else p=NULL; //强迫退出循环 }//while r->link=q->link; q->link=r; //r接在q的后面 return(f); //返回f } void InPut(int &n,int w[m]) { printf("please input the sum ofnode/n"); //提示输入结点数 scanf("%d",&n); //输入结点数 printf ("please input weight of everynode/n"); //提示输入每个结点的权值 for(int i=1;i<=n;i++) scanf("%d",&w[i]); //输入每个结点权值 } int main( ) { struct ptree *head; int n,w[m]; InPut(n,w); head=hafm(n,w); travel(head,0); printf("The length of the best path isWPL=%d", WPL);//输出最佳路径权值之和 return 1; }
转载于:https://blog.51cto.com/javacsh/1129213
最优二叉树——哈夫曼树相关推荐
- 6.6.1最优二叉树(赫夫曼树)
首先我们来看一个伪代码.这个是代表成绩的等级. 然后我们知道,每一次高考,学生的成绩分布应该接近某个比例,现在我们假如分别规律如下: 为此可以作出下面的这个树. 我们发现,概率分布主要是在70-79, ...
- 最优二叉树(赫夫曼树)
赫夫曼树的介绍(写的不好地方大佬请指教) 最优二叉树又称哈夫曼树,是带权路径最短的二叉树.根据节点的个数,权值的不同,最优二叉树的形状也不同. 图 6-34 是 3 棵最优二叉树的例子,它们共同的特点 ...
- 算法学习之最优二叉树(赫夫曼树)
概念 给定n个权值作为n个叶子节点,构造一颗二叉树,若该数的代全路径长度(wpl)达到最小,称这样的的二叉树为最优二叉树,也成霍夫曼树 霍夫曼树是带权路径长度最短的树,权值较大的节点离根较近 路径和路 ...
- 最优二叉树-哈夫曼树
关于最优二叉树, 一开始看书 , 做题 还是蒙的 后来多做几题大致就懂得了 , 百度百科给出的官方定义: 给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最 ...
- 最优二叉树(哈夫曼树)
哈夫曼树 哈夫曼树的定义 哈夫曼树的构造 哈夫曼树的特点 哈夫曼编码 哈夫曼树的定义 例如: 哈夫曼树就是要构造一棵WPL最小的二叉树. 哈夫曼树的构造 哈夫曼树构造时,每次把权值最小的两颗二叉树合并 ...
- 最有二叉树 哈夫曼树
最优二叉树 1.树的路径长度 树的路径长度是从树根到树中每一结点的路径长度之和.在结点数目相同的二叉树中,完全二叉树的路径长度最短. 2.树的带权路径长度(Weighted Path Leng ...
- 理论基础 —— 二叉树 —— 哈夫曼树与哈夫曼编码
[哈夫曼树] 1.相关概念 1)叶结点的权值:对叶结点赋予的一个有意义的数值量 2)二叉树的带权路径长度(WPL):设二叉树具有 n 个带权叶结点,从根结点到各叶结点的路径长度与相应叶节点权值的乘积之 ...
- 【id:180】【20分】D. DS二叉树--赫夫曼树解码(不含代码框架)
题目描述 已知赫夫曼编码算法和程序,在此基础上进行赫夫曼解码 在赫夫曼树的类定义中增加了一个公有方法: int Decode(const string codestr, char txtstr[]) ...
- 【id:179】【20分】C. DS二叉树--赫夫曼树的构建与编码(不含代码框架)
题目描述 给定n个权值,根据这些权值构造huffman树,并进行huffman编码 参考课本P147算法6.12 HuffmanCoding代码,注意数组访问是从位置1开始 要求:赫夫曼的构建中,默认 ...
最新文章
- 充分理解表达式——《狂人C》习题解答2(第二章习题5)
- 未来的黑科技计算机作文简写,写一篇英文作文。“未来计算机的发展”
- ASP.NET 链接 Access 数据库路径问题最终解决方案
- Selenium3 + Python3自动化测试系列一——安装Python+selenium及selenium3 浏览器驱动
- 数据库设计与开发概述
- 深度学习导论(3)PyTorch基础
- 关于Eclipes的Logcat无法打印消息的解决办法
- [Java Web]Error parsing HTTP request header Note: further occurrences of HTTP header parsing errors
- bat命令调用rar进行文件夹压缩
- java jni 调用_java jni调用过程分析
- 不用编程,实现ModbusTC方式与AB罗克韦尔的PLC标签方式通讯
- 栈和队列的共同处和不同处
- PTC指定位置安装许可服务器,PTC安装在终端服务器上的問題
- 微信小程序红包 php,微信小程序实现红包功能(后端PHP实现逻辑)
- 伸缩式起重机的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
- 古典概型几何概型伯努利概型条件概率
- 使用OMF(Oracle Managed Files,Oracle管理的文件)的场景示例
- 链表实现c语言通讯录管理系统,C++链表实现通讯录管理系统
- wxpython表格怎么分页_Word表格跨页断行如何排版?Word表格换页脱节怎么办?
- 一个80后SEOER对于SEO职业规划的三点想法