概念:
哈夫曼(Huffman)树(最优二叉树):带权路径长度最小
(1.)结点的权:赋予叶子结点有意义的值
(2.)结点的路径长度:从根节点到当前结点的边的个数
(3.)带权路径=结点的权值*路径长度
(4.)二叉树的带权路径长度:所有叶子节点的带权路径长度之和
构建:
局部最优
先找权值最小的,一次找两个,从下往上建树,并放回森林;左子女最小,右子女次小;重复上面步骤,直至森林中只剩下一棵二叉树
算法:
方法一:链式存储
过程:
1. 创建结构体,声明指针域和数据域;
left | 权值 | right |
左子女 字符域 右子女
typedef struct node{
char word;
int data;
struct node *left,*right;
}HalfNode;
2.创建森林:
建立F数组,存放结点地址
HalfNode **F; F=(HalfNode **)malloc(n*sizeof(HalfNode*));
3.初始化森林
for(int i=0;i<n;i++){
F[i]=()malloc();
F[i]->left=F[i]->right=NULL;
scanf("%c %d",&w,&t);
F[i]->word=w; F[i]->data=t; //权值
}
4.实现最优二叉树
(1.)找最小次小
k1从F[0]开始跑,假设k1为最小的,前提是F[i]不为空;k2为k1的下一个不为空的值,分情况讨论
for( loop=1;loop<n;loop++){
//k1放在第一个非NULL上,k2放在第二个非NULL上
for(k1=0;k1<n&&!F[k1];k1++);
for(k2=k1+1;k2<n&&!F[k2];k2++);
//找最小、次小值
for(int i=k2;i<n;i++){
if(F[i]){
if(F[i]->data<F[k1]->data){
k2=k1;
k1=i;
}
else if(F[i]->data<F[k2]->data){
k2=i;
}
}
}
(2.)生成二叉树
先生成双亲,再放回森林
p=()malloc();
p->word='x';
p->left=F[k1]; p->right=F[k2];
F[k1]=p; F[k2]=NULL; //放回森林
5.输出
if(root) {
printf("%c %d ",root->word,root->data);
print(root->left);
print(root->right);
}
else
return;
代码:
#include<stdio.h>
#include<stdlib.h>
typedef struct node{
char word;
int data;
struct node *left,*right;
}HalfNode;//建立结构体
//创建最优二叉树
HalfNode* CreatHalf(HalfNode** F,int n){
HalfNode *p;
int k1,k2,loop;
for( loop=1;loop<n;loop++){
//k1放在第一个非NULL上,k2放在第二个非NULL上
for(k1=0;k1<n&&!F[k1];k1++);
for(k2=k1+1;k2<n&&!F[k2];k2++);
//找最小、次小值
for(int i=k2;i<n;i++){
if(F[i]){
if(F[i]->data<F[k1]->data){
k2=k1;
k1=i;
}
else if(F[i]->data<F[k2]->data){
k2=i;
}
}
}
p=(HalfNode*)malloc(sizeof(HalfNode));
p->word='X';
p->data=F[k1]->data+F[k2]->data;
p->left=F[k1];
p->right=F[k2];
F[k1]=p;
F[k2]=NULL;
}
return F[k1];
}
void print(HalfNode *root){
if(root){
printf("%c %d ",root->word,root->data);
print(root->left);
print(root->right);
}
else
return;
}
int main(void){
int t,n;
printf("请输入元素个数\n");
scanf("%d",&n);
fflush(stdin);
char w;
HalfNode **F,*root;
//创建动态数组
F=(HalfNode**)malloc(n*sizeof(HalfNode*));
printf("请输入每个叶子的字符、权值\n");
for(int i=0;i<n;i++){
F[i]=(HalfNode*)malloc(sizeof(HalfNode));
F[i]->left=F[i]->right=NULL;
scanf("%c %d",&w,&t);
F[i]->word=w;
F[i]->data=t;
fflush(stdin); //清除键盘缓存区
}
root=CreatHalf(F,n);
print(root);
}
方法二:动态数组
1.首先创建结构体
typedef struct {
char word; //字符
int weight; //权值
int left,right,parent; //左子女、右子女、双亲结点
int *code; //存放编码
}Halfman;
2.数组:
word 序号 | data | left' | right | parent | code |
A 0 | 9 | -1 | -1 | 6 | 0 |
B 1 | 3 | -1 | -1 | 4 | 011 |
C 2 | 6 | -1 | -1 | 5 | 01 |
D 3 | 4 | -1 | -1 | 4 | 111 |
X 4 | 7(4和3) | 1 | 3 | 5 |
|
X 5 | 13(7和6) | 2 | 4 | 6 |
|
X 6 | 22(9和13) | 0 | 5 | -1 |
|
code域:动态数组,存放编码(0代表左子树,1则为右子树)
代码:
#include<stdio.h>
#include<stdlib.h>
typedef struct {
char word;
int weight;
int left,right,parent;
int *code;
}Halfman;
//创建Halfman树
Halfman* CreatHalfmantree(Halfman *F,int n){
int loop,i,k1,k2;
for(loop=0;loop<n-1;loop++){ //找最小次小
for(k1=0;k1<n+loop&&F[k1].parent!=-1;k1++);
for(k2=k1+1;k2<n+loop&&F[k2].parent!=-1;k2++);
for(i=k2;i<n+loop;i++){
if(F[i].parent==-1){
if(F[i].weight<F[k1].weight){
k2=k1;
k1=i;
}else if(F[i].weight<F[k2].weight){
k2=i;
}
}
}
F[i].word='X';
F[i].weight=F[k1].weight+F[k2].weight;
F[i].parent=-1;
F[k1].parent=i;
F[k2].parent=i;
F[i].left=k1;
F[i].right=k2;
}
return F;
}
//输出
void printHalfman(Halfman *F,int n){
for(int i=0;i<n;i++){
printf("%c ",F[i].word);
printf("%d ",F[i].weight);
printf("%d ",F[i].parent);
for(int j=1;j<=F[i].code[0];j++){
printf("%d ",F[i].code[j]);}
printf("\n");
}
}
//code域创建动态数组:存放编码
void Creatcode(Halfman* F,int n){
int *p;
int pa,c,i;
for(i=0;i<n;i++){
p=F[i].code=(int *)malloc(n*sizeof(int));
c=i;
p[0]=0;
pa=F[i].parent;
while(F[c].parent!=-1){ //循环条件
pa=F[c].parent;
if(F[pa].left==c)
p[++p[0]]=0;
else
p[++p[0]]=1;
c=pa;
}
}
}
int main(void){
int n,k1,k2;
Halfman *F;
printf("请输入叶子节点的个数\n");
scanf("%d",&n);
fflush(stdin); //清除键盘缓冲区
//初始化
F=(Halfman*)malloc((2*n-1)*sizeof(Halfman));
for(int i=0;i<n;i++){
F[i].left=F[i].right=F[i].parent=-1;
printf("请输入传输的字符:\n");
scanf("%c",&F[i].word);
fflush(stdin); //清除键盘缓冲区
printf("请输入传输的频率:\n");
scanf("%d",&F[i].weight);
fflush(stdin); //清除键盘缓冲区
}
//创建Halfman:森林
F=CreatHalfmantree(F,n);
Creatcode(F,n); //进行哈夫曼编码
printHalfman(F,n); //输出哈夫曼树
}