哈夫曼树是给定n个权值作为n的叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近
哈夫曼树一旦创建,就可以永久当解码使用所以无需频繁的修改哈夫曼树,哈夫曼树一定是采用树型结构
哈夫曼树的数据量并不是很大,使用int类型完全就可以足够,选择的有结构体数组,通过left,right,parent的成员变量来调用也可以使用指针的链式结构来完成。
通过宏定义类型HuffNode创建hf[10]的结构体数组,其中包括数据data,权重weight,左孩子left,右孩子right,父节点parent。通过这个结构体来定义结构。
通过宏定义类型HuffCode创建hcd[10]的结构体数组,其中包括哈夫曼树的编码数组cd[10],一个起点start
毫无疑问,哈夫曼树采用树形结构来存储数据,一组数据之中选择最小的两个数求和保存作为父节点,删除这两个子节点,加入父节点到原来的数组中,继续递归此种步骤,创建出树
考虑到哈夫曼树一旦创建成功就无需经常变化,我们就采用顺序结构,因为哈夫曼树需要经常访问结点,所以,就能很好的利用顺序结构的方便读取和避免存储地址浪费空间的问题。
HuffmanCreate创建哈夫曼树
Encoding编码
#include<stdio.h>
#include <stdlib.h>
#define MaxNum 10;
typedef struct{char data;int weighf;int left;int right;int parent;}HuffNode;
typedef struct{char cd[10];int start;}HuffCode;
HuffNode hf[10];
HuffCode hcd[10];
int HuffmanCreate(HuffNode *hf);
void Encoding(HuffNode hf[],HuffCode hcd[],int n);
int HuffmanCreate(HuffNode *hf){
int i,k,n,m1,m2,p1,p2;//i->控制循环n->元素个数
printf("请输入元素个数:");
scanf("%d",&n);
for(i=1;i<=n;i++){ /* 输入结点值和信息 */
getchar(); /* 接收回车 */
printf("第%d个元素的=>\n\t结点值:",i);
scanf("%c",&hf[i].data);
printf("\t权 重:");
scanf("%d",&hf[i].weighf);//输入权重
}
for(i=1;i<=2*n-1;i++)hf[i].parent=hf[i].left=hf[i].right=0;/* 对所有数组初始化 */
for(i=n+1;i<=2*n-1;i++){ //对所有输入数据以外的结点赋值
m1=m2=32767; /* 初始化,令m1、m2为整数最大值 */
p1=p2=1;
for(k=1;k<=i-1;k++){ /* 从数组hf[1]到hf[i-1]中找出 */
if(hf[k].parent==0) /* parent为0并且权值最小的两个结点 */
if(hf[k].weighf<m1){
m2=m1; /* m1为最小权值 */
p2=p1; /* p1为最小权值的位置 */
m1=hf[k].weighf; /* m1存放最小权值 */
p1=k;
}
else if(hf[k].weighf<m2){
m2=hf[k].weighf; /* m2为次小权值 */
p2=k; /* p2为次小权值的位置 */
}
}
hf[p1].parent=i; /* i分别赋给下标为p1、p2的数组中 */
hf[p2].parent=i;
hf[i].weighf=m1+m2; /* 新结点的的权值为最小权值和次小权值的和 */
hf[i].left=p1; /* p1为新结点的左孩子 */
hf[i].right=p2; /* p2为新结点的右孩子 */
}
printf("哈夫曼树已成功建立!\n");
return n; /* 返回结点个数 */
}
void Encoding(HuffNode hf[],HuffCode hcd[],int n)/* 哈夫曼编码 */{
HuffCode d;
int i,k,f,c;
for(i=1;i<=n;i++){ /* 对所有结点循环 */
d.start=n+1; /* 起始位置 */
c=i; /* 从叶结点开始向上 */
f=hf[i].parent;
while(f!=0){ /* 直到树根为止 */
if(hf[f].left==c)d.cd[--d.start]='0';/* 规定左树为代码0 */
else d.cd[--d.start]='1';/* 规定右树为代码1 */
c=f; /* c指孩子的位置 */
f=hf[f].parent; /* f指双亲的位置 */
}
hcd[i]=d;
}
printf("输出哈夫曼编码:\n");
for(i=1;i<=n;i++){ /* 输出哈夫曼编码 */
printf("%c:",hf[i].data); /* 先输出结点 */
for(k=hcd[i].start;k<=n;k++)/* 再输出其对应的编码 */
printf("%c",hcd[i].cd[k]);
printf("\n");
}
}
void Decoding(HuffNode ht[],HuffCode hcd[],int n)/* 哈夫曼译码 */
{
int f,m,k;
char c,ch[200]; /* c接收输入电文,ch存储 */
printf("请输入电文(0 or 1),以#为结束标志:\n");
c=getchar();
k=1;
while(c!='#') /* 单个字符循环输入,以'#'结束 */
{
ch[k]=c; /* 将单个字符依次存入ch字符串中 */
c=getchar();
k=k+1; /* ch数组下标后移 */
}
m=k; /* 标记数组存储末尾位置 */
f=2*n-1;
k=1; /* k记录电文字符的个数 */
printf("输出哈夫曼译码:\n");
while(k<m) /* k循环到数组末尾结束 */
{
while(ht[f].left!=0)/* 直到左孩子结点为0结束 */
{
if(ch[k]=='0') /* 若接收的字符为0,则存为左孩子 */
f=ht[f].left;
if(ch[k]=='1') /* 若接收的字符为1,则存为右孩子 */
f=ht[f].right;
k++; /* ch数组下标后移 */
}
printf("%c",ht[f].data);
f=2*n-1; /* 每次都从根结点开始查找 */
}
printf("\n");
}
int main(int argc,char* argv[])
{
int n,select,flag=0; /* flag为0时标记第一次选择功能 */
HuffNode ht[2*10]; /* 定义存放哈夫曼树的数组 */
HuffCode hcd[10]; /* 定义存放编码的数组 */
while(1)
{
printf("\t 请选择您所要实现的功能:(请输入1-4数字)\n");
printf("\t1---建立哈夫曼树\n");
printf("\t2---编码\n");
printf("\t3---译码\n");
printf("\t4---退出系统\n");
scanf("%d",&select);
if(select!=1&&select!=4&&flag==0)
{ /* 提示先建立哈夫曼树或退出 */
printf("请先建立哈夫曼树再选择其他功能!\n");
continue;
}
flag=1;
switch(select) /* 选择功能 */
{
case 1:n=HuffmanCreate(ht); break;
case 2:Encoding(ht,hcd,n); break;
case 3:Decoding(ht,hcd,n); break;
case 4:exit(0);
}
}
return 0;
}
Decoding译码
#include<stdio.h>
#include <stdlib.h>
#define MaxNum 10;
typedef struct{char data;int weighf;int left;int right;int parent;}HuffNode;
typedef struct{char cd[10];int start;}HuffCode;
HuffNode hf[10];
HuffCode hcd[10];
int HuffmanCreate(HuffNode *hf);
void Encoding(HuffNode hf[],HuffCode hcd[],int n);
int HuffmanCreate(HuffNode *hf){
int i,k,n,m1,m2,p1,p2;//i->控制循环n->元素个数
printf("请输入元素个数:");
scanf("%d",&n);
for(i=1;i<=n;i++){ /* 输入结点值和信息 */
getchar(); /* 接收回车 */
printf("第%d个元素的=>\n\t结点值:",i);
scanf("%c",&hf[i].data);
printf("\t权 重:");
scanf("%d",&hf[i].weighf);//输入权重
}
for(i=1;i<=2*n-1;i++)hf[i].parent=hf[i].left=hf[i].right=0;/* 对所有数组初始化 */
for(i=n+1;i<=2*n-1;i++){ //对所有输入数据以外的结点赋值
m1=m2=32767; /* 初始化,令m1、m2为整数最大值 */
p1=p2=1;
for(k=1;k<=i-1;k++){ /* 从数组hf[1]到hf[i-1]中找出 */
if(hf[k].parent==0) /* parent为0并且权值最小的两个结点 */
if(hf[k].weighf<m1){
m2=m1; /* m1为最小权值 */
p2=p1; /* p1为最小权值的位置 */
m1=hf[k].weighf; /* m1存放最小权值 */
p1=k;
}
else if(hf[k].weighf<m2){
m2=hf[k].weighf; /* m2为次小权值 */
p2=k; /* p2为次小权值的位置 */
}
}
hf[p1].parent=i; /* i分别赋给下标为p1、p2的数组中 */
hf[p2].parent=i;
hf[i].weighf=m1+m2; /* 新结点的的权值为最小权值和次小权值的和 */
hf[i].left=p1; /* p1为新结点的左孩子 */
hf[i].right=p2; /* p2为新结点的右孩子 */
}
printf("哈夫曼树已成功建立!\n");
return n; /* 返回结点个数 */
}
void Encoding(HuffNode hf[],HuffCode hcd[],int n)/* 哈夫曼编码 */{
HuffCode d;
int i,k,f,c;
for(i=1;i<=n;i++){ /* 对所有结点循环 */
d.start=n+1; /* 起始位置 */
c=i; /* 从叶结点开始向上 */
f=hf[i].parent;
while(f!=0){ /* 直到树根为止 */
if(hf[f].left==c)d.cd[--d.start]='0';/* 规定左树为代码0 */
else d.cd[--d.start]='1';/* 规定右树为代码1 */
c=f; /* c指孩子的位置 */
f=hf[f].parent; /* f指双亲的位置 */
}
hcd[i]=d;
}
printf("输出哈夫曼编码:\n");
for(i=1;i<=n;i++){ /* 输出哈夫曼编码 */
printf("%c:",hf[i].data); /* 先输出结点 */
for(k=hcd[i].start;k<=n;k++)/* 再输出其对应的编码 */
printf("%c",hcd[i].cd[k]);
printf("\n");
}
}
void Decoding(HuffNode ht[],HuffCode hcd[],int n)/* 哈夫曼译码 */
{
int f,m,k;
char c,ch[200]; /* c接收输入电文,ch存储 */
printf("请输入电文(0 or 1),以#为结束标志:\n");
c=getchar();
k=1;
while(c!='#') /* 单个字符循环输入,以'#'结束 */
{
ch[k]=c; /* 将单个字符依次存入ch字符串中 */
c=getchar();
k=k+1; /* ch数组下标后移 */
}
m=k; /* 标记数组存储末尾位置 */
f=2*n-1;
k=1; /* k记录电文字符的个数 */
printf("输出哈夫曼译码:\n");
while(k<m) /* k循环到数组末尾结束 */
{
while(ht[f].left!=0)/* 直到左孩子结点为0结束 */
{
if(ch[k]=='0') /* 若接收的字符为0,则存为左孩子 */
f=ht[f].left;
if(ch[k]=='1') /* 若接收的字符为1,则存为右孩子 */
f=ht[f].right;
k++; /* ch数组下标后移 */
}
printf("%c",ht[f].data);
f=2*n-1; /* 每次都从根结点开始查找 */
}
printf("\n");
}
int main(int argc,char* argv[])
{
int n,select,flag=0; /* flag为0时标记第一次选择功能 */
HuffNode ht[2*10]; /* 定义存放哈夫曼树的数组 */
HuffCode hcd[10]; /* 定义存放编码的数组 */
while(1)
{
printf("\t 请选择您所要实现的功能:(请输入1-4数字)\n");
printf("\t1---建立哈夫曼树\n");
printf("\t2---编码\n");
printf("\t3---译码\n");
printf("\t4---退出系统\n");
scanf("%d",&select);
if(select!=1&&select!=4&&flag==0)
{ /* 提示先建立哈夫曼树或退出 */
printf("请先建立哈夫曼树再选择其他功能!\n");
continue;
}
flag=1;
switch(select) /* 选择功能 */
{
case 1:n=HuffmanCreate(ht); break;
case 2:Encoding(ht,hcd,n); break;
case 3:Decoding(ht,hcd,n); break;
case 4:exit(0);
}
}
return 0;
}