哈夫曼树C语言实现学习笔记
学习了一些关于哈夫曼树的博文,简单记一篇笔记。
1、基本概念
哈夫曼树:又称最优二叉树,是一种带权路径长度最短的二叉树。
路径:若在一棵树中存在着一个结点序列 k1,k2,……,kj, 使得 ki是ki+1 的双亲(1<=i<j),则称此结点序列是从 k1 到 kj 的路径。
路径长度:从 k1 到 kj 所经过的分支数称为这两点之间的路径长度,它等于路径上的结点数减1。
权:树中节点被赋予的一个有着某种意义的实数。
树的带权路径长度:树的带权路径长度定义为树中所有叶子结点的带权路径长度之和,公式为:
如下图中树的带权路径长度 WPL = 9 x 2 + 12 x 2 + 15 x 2 + 6 x 3 + 3 x 4 + 5 x 4 = 122。
2、构造过程
假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为
(1)将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点)
(2)选出结点权值最小的两棵树合并,新树根节点权值为两子树权值之和,新树根节点替换之前两树结点。
(3)重复(2)直到只剩一棵树为止。
上图图解步骤如下
以最后的哈夫曼树左子树分支记0,右子树分支记1,将结点的路径上结点结合起来就是结点的哈夫曼编码:
A:00
B:01
C:100
D:1010
E:1011
F:11
注意:为了使得到的哈夫曼树的结构尽量唯一,通常规定生成的哈夫曼树中每个结点的左子树根结点的权小于等于右子树根结点的权。
3、代码实现
以下是哈夫曼树的相关实现:
tree.h
#ifndef __TREE_H__
#define __TREE_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXVALUE 1024 //最大权值
#define MAXBIT 1024 //哈夫曼编码最大长度
#define n 8 //叶子结点数目
#define m (2*n-1) //全部结点数目
typedef struct{
char name[1024];
int weight;
int parent;
int Lchild;
int Rchild;
}Htree;
typedef struct{
int bit[n]; //保存位串
int start; //编码起始位置
char name[1024];
}Hcode;
void SelectTree(Htree tree[],int position,int *node1,int *node2); //选择两个权值最小的树结点
void CreateTree(Htree tree[]); //构造哈夫曼树
void CodeTree(Hcode code[],Htree tree[]); //开始编码
void ShowTree(Htree tree[],Hcode code[]); //显示哈夫曼编码
#endif
tree.c
#include "tree.h"
void SelectTree(Htree tree[],int position,int *node1,int *node2)
{
*node1 = *node2 = 0;
int min1,min2;
min1 = min2 = MAXVALUE;
int i;
for(i = 0; i < position; i++)
{
if(tree[i].parent == -1)
{
if(tree[i].weight < min1)
{
min2 = min1;
min1 = tree[i].weight;
*node2 = *node1;
*node1 = i;
}else if(tree[i].weight < min2){
min2 = tree[i].weight;
*node2 = i;
}
}
}
}
void CreateTree(Htree tree[])
{
int i;
int node1,node2;
node1 = node2 = 0;
char name[1024];
int now_weight;
for(i = 0; i < m; i++)
{
tree[i].weight = 0;
tree[i].parent = -1;
tree[i].Lchild = -1;
tree[i].Rchild = -1;
}
printf("一共有%d个人\n",n);
for(i = 0; i < n; i++)
{
printf("请输入姓名:");
scanf("%s",name);
printf("请输入成绩:");
scanf("%d",&now_weight);
strcpy(tree[i].name,name);
tree[i].weight = now_weight;
}
for(i = n; i < m; i++)
{
SelectTree(tree,i,&node1,&node2);
tree[node1].parent = i;
tree[node2].parent = i;
tree[i].Lchild = node1;
tree[i].Rchild = node2;
tree[i].weight = tree[node1].weight + tree[node2].weight;
}
}
void CodeTree(Hcode code[],Htree tree[])
{
int i;
int parent_position,now_position;
Hcode cd;
CreateTree(tree);
for(i = 0; i < n; i++)
{
cd.start = n;
strcpy(cd.name,tree[i].name);
now_position = i;
parent_position = tree[i].parent;
while(parent_position != -1)
{
cd.start--;
if(tree[parent_position].Lchild == now_position)
{
cd.bit[cd.start] = '0';
}
else
cd.bit[cd.start] = '1';
now_position = parent_position;
parent_position = tree[now_position].parent;
}
code[i] = cd;
}
}
void ShowTree(Htree tree[],Hcode code[])
{
int i,j;
for(i = 0; i < n; i++)
{
printf("%s ",code[i].name);
for(j = code[i].start; j < n; j++)
printf("%c ",code[i].bit[j]);
printf("\n");
}
}
main.c
#include "tree.h"
int main()
{
Htree tree[m]; //有m个位置的空树
Hcode code[n]; //n个编码位置
CodeTree(code,tree);//编码
ShowTree(tree,code);//输出
return 0;
}
makefile
main:main.c tree.c
gcc -o main main.c tree.c
clean:
rm main
rebuild: clean main
下面是运行的效果截图