权值和带权路径长度
-
叶子结点的权值:对叶子结点赋予的一个有意义的数值量。
-
二叉树的带权路径长度:设二叉树具有n个带权值的叶子结点,从根结点到各个叶子结点的路径长度与相应叶子结点权值的乘积之和。
哈夫曼树
给定一组确定权值的叶子结点,带权路径长度最小的二叉树
构建哈夫曼树
n个结点权值W1,W2,…,Wn
第一步:将这n个结点分别作为仅含一个结点的二叉树,构成森林F。
第一步:构造一个新结点,从F中选取两颗权值最小的树作为新结点的左右子树,并且将新结点的权值置为其左右子树上根结点的权值之和。
第三步:从F中删除刚才选出的两棵树,同时将新得到的树加入F中。
第四步:重复第二步和第三步,直到F中只剩下一棵树为止。
哈夫曼树特点
-
权值越大的叶子结点越靠近根结点,而权值越小的叶子结点越远离根结点。
-
构造过程中共新建了n-1个结点,哈夫曼树结点总数为n+n-1=2n-1
-
只有度为0(叶子结点)和度为2(分支结点)结点,不存在度为1的结点(每次构造都选择两棵树作为新结点的孩子,故不可能存在度为1的结点)
特点二另一种理解:
在特点三的基础上结合二叉树的性质(非空二叉树上的叶子结点数等于度为2的结点数加1,即n0=n2+1)
哈夫曼树的总结点树n=n0+n2=n0+n0-1=2n0-1
即为哈夫曼树特点2
简单例题
3个结点权值(2,4,1),构建哈夫曼树
实现代码(C实现)
#include<stdio.h>
#include<stdlib.h>
struct element
{
int weight;//权重
int parent,lchild,rchild;//双亲,孩子 保存的是下标
};
void select(struct element huffTree[],int n,int* min,int* mi)
{
int tw[n];
int tn[n];
int i,k,j=0;
for(i=0;i<n;i++)
{
if(huffTree[i].parent==-1)
{
tn[j]=i;
tw[j]=huffTree[i].weight;
j++;
}
}
int t;
for(i=0;i<j-1;i++)
for(k=0;k<j-1-i;k++)
{
if(tw[k]>tw[k+1])
{
t=tw[k];
tw[k]=tw[k+1];
tw[k+1]=t;
t=tn[k];
tn[k]=tn[k+1];
tn[k+1]=t;
}
}
*min=tn[0];
*mi=tn[1];
}
void HuffmanTree(struct element huffTree[],int w[],int n)
{
//初始化所有结点的项目为-1
int i,min,mi;
for(i=0;i<2*n-1;i++)
{
huffTree[i].lchild=-1;
huffTree[i].rchild=-1;
huffTree[i].parent=-1;
}
//初始化前n个结点的权值
for(i=0;i<n;i++)
huffTree[i].weight=w[i];
for(i=n;i<2*n-1;i++)
{
select(huffTree,i,&min,&mi);
printf("最小下标:%d,次小下标:%d\n",min,mi);
huffTree[i].weight=huffTree[min].weight+huffTree[mi].weight;
huffTree[min].parent=i;
huffTree[mi].parent=i;
huffTree[i].lchild=min;
huffTree[i].rchild=mi;
}
}
int main()
{
int n=1;
printf("请输入结点个数:");
scanf("%d",&n);
printf("请输入%d个权值:",n);
int w[n];
int i;
for(i=0;i<n;i++)
scanf("%d",&w[i]);
struct element huffTree[2*n-1];
HuffmanTree(huffTree,w,n);
printf("\nweight\tparent\tlchild\trchild\n");
for(i=0;i<2*n-1;i++)
{
printf("%d\t%d\t%d\t%d\n",huffTree[i].weight,huffTree[i].parent,huffTree[i].lchild,huffTree[i].rchild);
}
system("pause>nul");
return 0;
}
运行截图(数字是下标,-1代表没有)