构造哈夫曼树
首先,我们需要了解哈夫曼树是什么:
一.相关知识点
路径: 路径是指从一个节点到另一个节点的分支序列。
路径长度: 指从一个节点到另一个结点所经过的分支数目。 ,从根节点到a的分支数目是2,
数的路径长度: 树中所有结点的路径长度之和为树的路径长度PL 如图pl为10
节点的权: 给树的每个结点赋予一个具有某种实际意义的实数,我们称该实数为这个结点的权
带权路径长度: 从树根到某一结点的路径长度与该节点的权的乘积,叫做该结点的带权路径长度
树的带权路径长度: 树的带权路径长度为树中所有叶子节点的带权路径长度之和。
路径长度为0,最多有一个(根)
路径长度为1,最多有2个(两个孩子)
路径长度为2,最多有4个叶结点。
构造Huffman树的步骤:
1) 根据给定的n个权值,构造n棵只有一个根结点的二叉树,n个权值分别是这些二叉树根结点的权;
2) 设F是由这n棵二叉树构成的集合,在F中选取两棵根结点权值最小的树作为左、右子树,构造成一颗新的二叉树,置新二叉树根结点的权值等于左、右子树根结点的权值之和。为了使得到的哈夫曼树的结构唯一,规定根结点权值最小的作为新二叉树的左子树。
3) 从F中删除这两棵树,并将新树加入F;
4) 重复2)、3)步,直到F中只含一棵树为止,这棵树便是Huffman树。
说明:n个结点需要进行n-1次合并,每次合并都产生一个新的结点,最终的Huffman树共有2n-1个结点。
如何构建哈夫曼树:在网上找了个比较好用的图片:
如上即可较为清晰的理解最优二叉树的构造了。
三.哈夫曼树代码的实现。
我们给出5个值为:2 3 5 7 8的权值,构造最优二叉树。
从上图可以看出:我们有权值:weight,左右孩子:LChild,RChild,双亲结点:parent,
所以:我们构成了一个哈夫曼树的结点结构HTNode:
Typedef struct
{
Int weight;
Int parent , LChild,RChild;
}HTNode, *HuffmanTree;
void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n){//根据输入的结点的权值和个数来构建赫夫曼树
int m=2*n-1;//n个叶子,有2*n-1个结点
int i; HuffmanTree p;
for(p=HT+1,i=1;i<=n;i++,p++,w++) {//叶子结点赋值
p->weight=*w;p->parent=0;p->lchild=0;p->rchild=0;
}
for(;i<=m;i++,p++) {//非叶子结点初始化
p->weight=0;p->parent=0;p->lchild=0;p->rchild=0;
} 此图为上述两个循环的意思。
for(i=n+1;i<=m;i++){循环次数为n-1次,进行构造哈夫曼树。
select(ht,i-1,&s1,&s2);
/*选择parent为0且weight最小的两个结点,其序号分别赋值给s1,s2*/
ht[s1].parent=i;
ht[s2].parent=i;
ht[i].Lchild=s1;
ht[i].Rchild=s2;
上述代码是先找出两个最小值,先将其合并,构造出一个有根节点和两个左右结点的树,再取出其根节点再次合并
ht[i].weight=ht[s1].weight+ht[s2].weight;
/*哈夫曼树建立完毕*/
最后得出:
}
/*从叶子节点到根逆向求每个字符或者指令的哈夫曼编码*/
hc=(haffmancode)malloc(n*sizeof(char*));申请一个字符给指针,字符的区域hc
给hc分配n个字符编码的头指针
/*分配n个字符编码的头指针*/
cd=(char*)malloc(n*sizeof(char));cd为中间辅助单元,n个结点的位置为cd
cd[n-1]='\0';cd的最后一位为编码结束符,
for(j=0;j
下面给出具体实现代码:
#include
#include
#include
#include
#include
#define MAX_NUM 100//最大数字个数为100
#define inf 2000000000 //最大值为
using namespace std;
typedef struct {
unsigned int weight;//权值
unsigned int parent,lchild,rchild;//父节点,孩子结点的权值
}HTNode,*HuffmanTree;
typedef char * * HuffmanCode;//二维字符数组
int s1,s2;//最小的两个结点
void Select(HuffmanTree &HT,int x){//选出无父结点,并且权值最小的两个结点,赋值给s1,s2
int i,min1=inf,min2=inf;
for(i=1;i<=x;i++){//找最小
if(HT[i].weightweight=*w;p->parent=0;p->lchild=0;p->rchild=0;
}
for(;i<=m;i++,p++) {//非叶子结点初始化
p->weight=0;p->parent=0;p->lchild=0;p->rchild=0;
}
for(i=n+1;i<=m;i++){
Select(HT,i-1);//选出最小的两个无父节点的结点
HT[s1].parent=i;HT[s2].parent=i;
HT[i].lchild=s1;HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
//----------下面是将每个结点的赫夫曼编码存入二维字符数组
HC=(HuffmanCode)malloc((n+1)*sizeof(char *));//申请一段以HC为首地址的内存,可以看成二维字符数组 ,这里先申请了第一维
char *cd=(char *)malloc(n*sizeof(char));//申请一段临时工作空间
cd[n-1]='\0';//编码结束符
for(i=1;i<=n;i++){
int start=n-1,c,f;
for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent){//从叶子到根逆向求编码
if(HT[f].lchild==c)cd[--start]='0';//如果当前结点是父节点的左孩子,则存一个1
else cd[--start]='1';//反之
}
HC[i]=(char *)malloc((n-start)*sizeof(char));//申请第二维
strcpy(HC[i],&cd[start]);//将编码从工作空间存入赫夫曼编码表中
}
free(cd); //释放临时空间
}
int main(){
HuffmanTree HT;//自定义结构体 ht
HuffmanCode HC;
int w[MAX_NUM],n;
int i;
printf("输入结点的个数:\n");
scanf("%d",&n);
printf("输入每个结点的权值:\n");
for( i=0;i
因为刚好学到这里,便做了个总结。