哈夫曼树是特殊二叉树,又称最优二叉树。该树的所有叶子结点都有权值,从而构造出带权路径最短的二叉树。
- 路径长度:树中路径上分支数目。
- 叶子结点权值:为每一个叶子结点赋予的一个数值。(哈夫曼树中,分支结点不需要赋予权值。)
- 权值不给定时,字符串中,字符出现频率可设为权值,根权值最终等于叶子结点权值之和。(用于验证建立是否正确)。
- 树的带权路径长度:树中所有叶子结点的权值乘以该结点的路径长度之和。WPL=Wk*Lk (求和);
k->n,Wk为第k个叶子结点的权值,Lk为从根结点到第k结点的路径长度。 -
总n个结点,n-1次合并后,只剩一结点。
问题
-
n同为叶子节点数,有性质3得总结点数N=n-1?
-
selectmin函数中,p1,p2未使用引用或指针导致运行错误。
-
数组做形参时的接收格式错误编译失败。
-
creathfmt函数中,for循环i未从n起始,运行错误。
-
t[i].weight =t[p1].weight +t[p2].weight ,大意将 t[p2].weight 写成t[p2].rchild 导致结果持续错误。
-
hfnode(htnode t[],int i,int j) 中,
if(t[j].parent !=-1){ i=j,hfnode(t,i,j);//递归 为什么是逗号????? }
//递归 ,为什么是逗号?????不理解句意。 -
mannode(htnode t[],int n)中,
printf("\n%i的编码为:",t[i].weight );
%i的用途??
解决
- 混肴二叉树性质3,性质3内容为任意一颗二叉树,叶子节点数n,度为二的节点数n2,那么n=n2+1.或者是度为二的结点数n2=n-1。此处求总的结点数2*n-1。
- p1,p2作用是记录下标,将最终下标带回使用,形参改变,要引起实参改变,所以要使用引用或者指针。
- 数组做参数的接收格式:例一维数组 (int data[ ]);二维数组(int data[ ][n]),n为具体数。
- creathfmt函数中,for循环i从n起始,因为新生成的结点依次从n后插入。
- 逐步个对照代码检查错误然后更正,细心。
- 递归处使用分号也没有问题。
由键盘输入各个字符的权值,编程构造哈夫曼树。
#include<stdio.h>//数组做形参时的结构
#define MAX 100
typedef struct{
int weight;//结点权值
int lchild,rchild,parent;//左右孩子结点在组数中的序号,双亲在数组中序号
//parent域判断结点是否已加入要建立的哈夫曼树中,初始值为-1,
}htnode;
//全局变量int n;
//哈夫曼树初始化子函数
void inithfmt(htnode t[],int n){//树中所有结点都要初始化!!!!!
int i;
for(i=0;i<2*n-1;i++){//满二叉树中总结点数2*n-1 性质2???
t[i].weight =0;
t[i].lchild =-1;
t[i].rchild =-1;
t[i].parent =-1;
}
}
//输入权值
void inputweight(htnode t[],int n){
int w,i;
for(i=0;i<n;i++){//叶子结点赋值 i是下标
printf("输入%d个权值:",i+1);
scanf("%d",&w);
getchar();//接收回车键
t[i].weight =w;
}
}
//选择最小值两个
void selectmin(htnode t[],int i,int &p1,int &p2){
long min1=88888,min2=88888;//预设两个值 使他大于可能出现的最大权值
int j;
for(j=0;j<=i;j++){//比较至所有的叶子结点 所以取等号!!!!!
if(t[j].parent ==-1){//结点未使用则继续
if(min1>t[j].weight ){
min1=t[j].weight ;//权值比设定值小 改变设定值
p1=j;//p1存下标 带回使用
}
}
}
for(j=0;j<=i;j++){
if(t[j].parent ==-1){
if(min2>t[j].weight &&j!=p1){//附加条件 与选择的第一个值(下标不同)
min2=t[j].weight ;
p2=j;//记录下标
}
}
}
}
//构造哈夫曼树,有t[2*n-1]为其根结点
void creathfmt(htnode t[],int n){
int i,p1,p2;
inithfmt(t,n);
inputweight(t,n);
for(i=n;i<2*n-1;i++){//i从n开始,因为新值依次插入数组尾端
selectmin(t,i-1,p1,p2);
t[p1].parent =t[p2].parent =i;
t[i].lchild =t[p1].weight;
t[i].rchild =t[p2].weight;
t[i].weight =t[p1].weight +t[p2].weight ;//大意 t[p2].weight 写成t[p2].rchild
}
}
//输出向量状态表
void print(htnode t[],int n){
int i;
printf("\n哈夫曼树的各边显示\n");
for(i=0;i<2*n-1;i++){
while(t[i].lchild !=-1){
printf("(%d,%d),(%d,%d)\n",t[i].weight ,t[i].lchild ,t[i].weight ,t[i].rchild );
break;
}
}
}
//哈夫曼编码函数
void hfnode(htnode t[],int i,int j){
j=t[i].parent ;
if(t[j].rchild ==t[i].weight ){
printf("1");
}
else{
printf("0");
}
if(t[j].parent !=-1){
i=j,hfnode(t,i,j);//递归 为什么是逗号?????
}
}
//求哈夫曼编码
void mannode(htnode t[],int n){
int i,j,a;
printf("\n输入的权值对应的哈夫曼编码;");
for(i=0;i<n;i++){
j=0;
a=i;
printf("\n%i的编码为:",t[i].weight );
hfnode(t,i,j);
i=a;
}
}
main(){
int n;
htnode t[MAX];
printf("\n输入权值个数");//等价于叶子个数
scanf("%d",&n);
creathfmt(t,n);
print(t,n);
mannode(t,n);
}