一、 设计思想
算法的设计思路:
1. 哈弗曼树的建立:打开文本文档,扫描文本文档中的字符并将其存入数组S中,扫描数组S统计出各种字符的个数。有多少种不同的字符就有多少个叶子结点,字符的个数就是叶子结点的权值。然后,每次找出权值最小的两个叶子结点进行合并,合并后的结点作为新的结点参与合并,直到没有结点合并为止,这样就构造出了哈弗曼树。
2. 哈弗曼树的编码:对于每一个结点都从叶子结点开始到根结点为止来判断路径上每条树枝是左树枝还是右树枝。如果是左树枝就将0填入编码数组中,如果是右树枝就将1填入编码数组中。然后将编码数组输出即可。
3. 解码算法:解码是由根结点开始到叶子结点为止的判断过程。扫描只包含0和1的字符串,如果是0就将待查结点移动至其做孩子,如果是1就将待查结点移动至其右孩子,重复上述过程直到找到叶子结点为止。
4. 哈弗曼树的输出:采用先序递归遍历哈弗曼树来输出各个结点。若是根结点就直接输出,若不是根结点则输出(2*lev-2)个空格和一个竖线一个行线后再输出该结点。其中变量lev代表哈弗曼树的层次结构,根结点为第0层。
二、源代码
/*---------------------哈夫曼树的编码译码----------------------------*/
/*---------------------定义哈夫曼树中所需的变量----------------------*/
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define M 100 /*定义文本中字符的最大个数*/
#define MAX 1000 /*定义总的编码的最大长度*/
char c[M]; /*建立用来暂时存放译码的数组*/
char u[M]; /*建立一个特殊的数组来暂时存储文档中的字符*/
char s[M]; /*建立一个特殊的数组来暂时存储文档中的字符*/
int number=0; /*用number来计算从文档中读取得总的字符总数*/
/*----------------建立哈夫曼编译码器所需的所有模块函数---------------*/
/*建立哈夫曼树的几本节点结构*/
typedef struct
{
int data; /*节点权值*/
int parent;/*标志双亲节点的地址域*/
char ch; /*用来存放各节点的关键字*/
int left;/*左孩子下标*/
int right;/*右孩子下标*/
}hnode ;
/*初始化huffmantree*/
void InitHuffman(hnode r[])
{ int i;
for(i=1;i<=M;i++)
{
r[i].data=0;
r[i].parent=0;
r[i].ch=5;
r[i].left=0;
r[i].right=0;
}
}
/*建立用于储存哈夫曼树关键字的文档文件*/
int text()
{
int i=1;
int k=0;/*字符总数*/
FILE *fp;
fp=fopen("D://huffman.txt","r"); /*打开已经建立好的文件文档*/
s[i]=fgetc(fp);
while(s[i]!=EOF) /*通过while循环逐个读取文档中的所有字符*/
{
s[++i]=fgetc(fp);
k++;
}
return(k); /*k用来计算文档中的总的字符总量*/
}
/*******************************************************************************/
/*-----------------------------------建立哈夫曼树----------------------------------------*/
int Huffman(hnode r[])
{
int n,m1,m2,x1,x2,j;
int k;/*字符总数*/
int i=1;
int count=1;/*统计各个字符的数量*/
int t;
FILE *fp;
fp=fopen("D://huffman.txt","r"); /*打开存于硬盘中的哈夫曼存档,若无,则通过键盘重新输入*/
if(fp!=NULL)
{
printf("*******The numble of the chars is:");
k=text(); /*读取文档中的参数*/
printf("%d/n",k);
printf("********Output the list*********/n");
printf("*char* *numble* *probability*/n");
for(t=1;t<k;t++) /*通过两个For循环实现两个字符是否相同的实现*/
{
u[t]=fgetc(fp);
while(u[t]!=EOF) /*判断当前查找数据是否为尾*/
{
u[++t]=fgetc(fp);
/*取出下一个文档中的字符*/
}
for(t=1;t<=k;t++)
{
if((u[t]!=0)&&(u[t]!=EOF)) /*循环条件为当前值不为零,且当前值不为空*/
{
for(j=1;j<k;j++)
{
if((u[t]==u[t+j])&&(u[t]!=EOF)) /*当前字符与数组中的其他字符进行比较*/
{
count++; /*若找到相同的字符,计数器自增一*/
u[t+j]=0;
}
}
printf(" %c %d %5.2f/n",u[t],count,(double)count/(double)k);
r[i].data=count; /*将确定好的权值和关键字存入哈夫曼数组*/
r[i].ch=u[t];
r[i].left=0;
r[i].right=0;
r[i].parent=0;
count=1; /*将计数器还原*/
i++;
}
}
}
i=i-1;
number=i;/*叶子节点数*/
i=0;
n=number;
}
while(i<n-1) /*根节点合并n-1次*/
{
m1=32767;
m2=32767;
x1=0;
x2=0;
for(j=1;j<=n+i;j++) /*通过for循环确定权值最小的点*/
{
if((r[j].data<m1)&&(r[j].parent==0))
{
m2=m1;
x2=x1;
m1=r[j].data;
x1=j;
}
else
{
if((r[j].data<m2)&&(r[j].parent==0))
{
m2=r[j].data;
x2=j;
}
}
}
i++;
r[x1].parent=n+i; /*合并后的节点为原节点的双亲节点*/
r[x2].parent=n+i; /*合并后的节点为原节点的双亲节点*/
r[n+i].data=r[x1].data+r[x2].data; /*合并两棵二叉树的权值*/
if(x1<x2)
{
r[n+i].left=x1; /*合并后的节点的左孩子为x1节点*/
r[n+i].right=x2; /*合并后的节点的右孩子为x2节点*/
}
else
{
r[n+i].left=x2; /*合并后的节点的左孩子为x2节点*/
r[n+i].right=x1; /*合并后的节点的右孩子为x1节点*/
}
r[n+i].parent=0;
}
n=n+i;
return (n);/*节点总数*/
}
/*******************************编码*****************************************/
typedef struct
{
int bits[M];
int start;
}huffcode;
void huffmancode(hnode r[],int n)
{ huffcode huff_code[M];
huffcode cd;
int c,p,i,j;
int k;
int t;
int a,b;
int ch;
int arry[MAX]; /*用于存编码串*/
int h=1;
int num;
printf("*************The coding is***********/n");
for(i=1;i<=(n+1)/2;i++)/*(n+1)/2是叶子结点数*/
{
cd.start=n;
c=i;
p=r[c].parent;
while(p!=0)
{
if(r[p].left==c)
cd.bits[cd.start]=0;
else
cd.bits[cd.start]=1;
cd.start=cd.start-1;
c=p;
p=r[p].parent;
}
for(j=cd.start+1;j<=n;j++)
huff_code[i].bits[j]=cd.bits[j];
huff_code[i].start=cd.start;
}
for(i=1;i<n-1;i++)
{
printf("%c ",r[i].ch);
for(j=huff_code[i].start+1;j<=n;j++)
{
printf("%d",huff_code[i].bits[j]);
}
printf("/n");
}
/**输出编码***/
k=text();
printf("**********coding the words**********:/n");
for(t=1;t<=k;t++) /*输出编码*/
{
for(i=1;i<=(n+1)/2;i++)
{
if(s[t]==r[i].ch)
{
for(j=huff_code[i].start+1;j<=n;j++)
{
printf("%d",huff_code[i].bits[j]);
ch=huff_code[i].bits[j] ;
arry[h]=ch;
h++;
}
}
}
}
num=h-1;
printf("/n*********The numble of the code is:%d",num);
printf("/n");
/****译码**/
printf("**********decoding the code**********/n");
b=n;
/*printf("%d",r[b].right);*/
for(h=1;h<=num;h++)
{
if(arry[h]==0)
{
a=r[b].left; /*使待查节点移动至其左孩子*/
b=a;
}
else
{
a=r[b].right; /*使待查节点移动至其右孩子*/
b=a;
}
if((r[b].left==0)&&(r[b].right==0))
{
printf("%c",r[b].ch); /*输出查找到叶子节点的关键字*/
b=n;
}
}
printf("/n");
/***/
}
/***************打印树**********************************/
void OutputTree(hnode* r,hnode *root,int lev)
{
int i=0;
if(root->left!=0)
{
if(lev==0)
{
printf("%c/n",root->ch);
}
else
{
for(i=0;i<(2*lev-2);i++)
printf(" ");
printf("|_%c/n",root->ch);
}
OutputTree(r,&r[root->left],lev+1);
OutputTree(r,&r[root->right],lev+1);
}
else
{
for(i=0;i<(2*lev-2);i++)
printf(" ");
printf("|_%c/n",root->ch);
return;
}
}
/**********************************************************/
void main()
{ int n;
int lev=0;
hnode Node[100],root;
InitHuffman(Node);
n=Huffman(Node);
root=Node[n];
huffmancode(Node,n);
printf("******Output the HuffmanTree******/n");
OutputTree(Node,&root,lev) ;
getchar();
}