您的位置:首页 > 其它

简单哈夫曼 编/译码系统的设计与实现

2017-06-21 16:52 911 查看
问题描述

利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(解码)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站设计一个哈夫曼编译码系统。

基本要求

(1)初始化(Initialzation)。从数据文件DataFile.data中读入字符及每个字符的权值,建立哈夫曼树HuffTree;

(2)编码(EnCoding)。用已建好的哈夫曼树,对文件ToBeTran.data中的文本进行编码形成报文,将报文写在文件Code.txt中;

(3)译码(Decoding)。利用已建好的哈夫曼树,对文件CodeFile.data中的代码进行解码形成原文,结果存入文件Textfile.txt中;

(4)输出(Output)。输出DataFile.data中出现的字符以及各字符出现的频度(或概率);输出ToBeTran.data及其报文Code.txt;输出CodeFile.data及其原文Textfile.txt;

文件说明

DataFile.data(这里只有26个小写字母)


26

a 43 b 11 c 23 d 17 e 57 f 9 g 13 h 15 i 38 j 1 k 6 l 28 m 15 n 34 o 37 p 16 q 1 r 39 s 29 t 35 u 19 v 5 w 7 x 1 y 9 z 1


ToBeTran.txt:

do not aim for success if you want it

just do what you love and believe in and it will come naturally

suffering is the most powerful teacher of life

CodeFile.txt:(就是ToBeTran.txt的编码,这里等于做了双向验证)

100011011 100110111010 1111110101111 11000010111110 0110110010001000101001100110 1101110000 110001101111001 001011111110011010 11011010

000000001100101101010 100011011 0010110111011111010 110001101111001 00111011000001010 1111100110001 0000101000111101010000001010 11011001 1111100110001 11011010 001011110100110011 0001101101111010 100111111010110011110111100110011110001

01101100111000011000001011101101100100100 11010110 101001110010 01111101101101010 1000010110010110101110110000110010011 101001011110001011100101110 1011110000 00111101110000010

代码示例

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
const int maxn=100;

typedef struct Node{
int weight;//权值
int parent;//父节点的序号,为0表示根节点
int lchild,rchild;//左右孩子节点的序号,为0的是叶子节点
}HTNode,*HuffmanTree;//用来存储每个叶子节点的霍夫曼编码

char HC[maxn][maxn+5];//用来存储每个节点的霍夫曼编码

struct Ans{
char ch;
int wet;
}ans[maxn];

//从HT数组的前k个元素中选出weight最小且parent为0的元素,并将该元素的下标返回
int min(HuffmanTree HT,int k)
{
int i=0;
int min;//用来存放weight最小且parent为0的元素的下标
int min_weight;//用来存放weight最小且parent为0的元素的weight值的temp

//注意应先将第一个parent为0的元素的weight值赋给min_weight,留作以后比较用
//不要直接将HT[0].weight赋给min_weight了(这是个常用的技巧)
while(HT[i].parent!=0) i++;
min_weight=HT[i].weight;
min=i;

//选出weight最小且parent为0的元素,并将其序号赋给min
for(;i<k;++i){
if(HT[i].weight<min_weight&&HT[i].parent==0){
min_weight=HT[i].weight;
min=i;
}
}

//注意,选出weight最小的元素后,将其parent置1,使得下一次求第二小时将其排除在外
HT[min].parent=1;
return min;//返回下标
}

void select_min(HuffmanTree HT,int k,int &min1,int &min2)//&为引用
{
min1=min(HT,k);
min2=min(HT,k);
}

HuffmanTree create_HuffmanTree(struct Ans *p,int n)
{
//一颗有n个叶子节点的霍夫曼树共有2n-1个节点(特点)
int total=2*n-1;
HuffmanTree HT=(HuffmanTree)malloc(total*sizeof(HTNode));
if(!HT){
printf("HuffmanTree malloc failed!\n");
}

int i;
for(i=0;i<n;++i){//先构造n个叶节点
HT[i].parent=0;//parent后面会改变
HT[i].lchild=0;
HT[i].rchild=0;
HT[i].weight=p->wet;
p++;
}

//HT
,HT[n+1]...HT[2n-2]中存放的是中间构造出的每棵二叉树的根节点
for(;i<total;++i){
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
HT[i].weight=0;
}

int min1,min2;//用来保存每一轮选出的两个weight最小且parent为0的节点下标

//每一轮比较后选择出min1和min2构成一课二叉树,最后构成一棵赫夫曼树
for(i=n;i<total;++i){
select_min(HT,i,min1,min2);//寻找i之前的结点
HT[min1].parent=i;
HT[min2].parent=i;//两个最小权值的结点构成子树

//注意这里左孩子和右孩子可以反过来,构成的也是一棵赫夫曼树,只是所得的编码不同
HT[i].lchild=min1;
HT[i].rchild=min2;
HT[i].weight=HT[min1].weight+HT[min2].weight;
}
return HT;
}

void HuffmanCoding(HuffmanTree HT,int n)
{
char temp[maxn+5];//临时空间,用来保存每次求得的一个赫夫曼编码串
temp[maxn]='\0';//编码结束符,亦是字符数组的结束标志

//下面求每个字符的赫夫曼编码
for(int i=0;i<n;++i){//从叶子向上找根
int current=i;//定义当前访问的结点
int fa=HT[i].parent;//定义当前节点的父节点
int start=maxn;//每次编码的位置,初始为编码结束符的位置
//从叶子节点遍历赫夫曼树直到根节点
while(fa!=0)
{
if(HT[fa].lchild==current)
temp[--start]='0';
else temp[--start]='1';
current=fa;
fa=HT[current].parent;
}
strcpy(HC[i],temp+start);//将编码串从最后的start编码~\0复制到HC上
}
}

int n;//需要编码的字符数
void Initialzation()
{
FILE *fp1;
fp1=fopen("DataFile.txt","rb");
printf("从文件DataFile.txt中读取编码的字符种类个数\n");
fscanf(fp1,"%d",&n);

printf("以及这%d个字符及其权值(整型)\n",n);
for(int i=0;i<n;++i){//对于编码,权值和出现频率有关
fscanf(fp1," %c %d",&ans[i].ch,&ans[i].wet);//注意%c前面有空格
}
HuffmanTree HT=create_HuffmanTree(ans,n);//用数组生成霍夫曼树
HuffmanCoding(HT,n);//求解每个字符的霍夫曼编码

printf("各字符对应权值的霍夫曼编码为(按照输入顺序):\n");
for(int i=0;i<n;++i){
printf("%c :",ans[i].ch);
puts(HC[i]);
}
fclose(fp1);
printf("\n******************************************************\n\n");
}

void EnCoding()
{
//char ch[maxn*maxn],temp;
printf("下面对文件ToBeTran.data中的文本进行编码形成报文\n");
printf("报文写在文件Code.txt中\n");
FILE *fp2,*fp3;
fp2=fopen("ToBeTran.txt","rb");
fp3=fopen("Code.txt","w");
//fscanf(fp2,"%s",temp);
char ch;
do
{
ch=fgetc(fp2);
/* read a char from the file */
if(ch==' ') fprintf(fp3," ");
else if(ch=='\n') fprintf(fp3,"\n");
else fprintf(fp3,"%s",HC[ch-'a']);
}while(ch!=EOF);
fclose(fp2);
fclose(fp3);
}

int fun(char *temp)//在HC中找是否有匹配,如果有返回下标,没有返回-1
{
for(int i=0;i<n;++i){
if(strcmp(HC[i],temp)==0) return i;
}
return -1;
}

void Decoding()
{
printf("下面对文件CodeFile.data中的代码进行解码形成原文\n结果存入文件Textfile.txt中\n");
FILE *fp4,*fp5;
fp4=fopen("CodeFile.txt","rb");
fp5=fopen("Textfile.txt","w");
char ch;
char temp[maxn];
char *s=temp;
memset(temp,0,sizeof(temp));
//while(fgets(temp,maxn*maxn,fp4))//读到换行符,并加上\0,存入temp
do
{
ch=fgetc(fp4);
if(ch=='\n') memset(temp,0,sizeof(temp)),s=temp,fprintf(fp5,"\n");
else if(ch==' ') memset(temp,0,sizeof(temp)),s=temp,fprintf(fp5," ");
else{
*s++=ch;
if(fun(temp)>=0) fprintf(fp5,"%c",'a'+fun(temp)),memset(temp,0,sizeof(temp)),s=temp;
else continue;
}
}while(ch!=EOF);
fclose(fp4);
fclose(fp5);
}

void Output()
{
//输出DataFile.data中出现的字符以及各字符出现的频度(或概率);
//输出ToBeTran.data及其报文Code.txt;
//输出CodeFile.data及其原文Textfile.txt;
printf("下面输出DataFile.data中出现的字符以及各字符出现的概率\n");
int sum=0;
for(int i=0;i<n;++i){
sum+=ans[i].wet;
}
for(int i=0;i<n;++i){
printf("%c :%lf\n",ans[i].ch,ans[i].wet*1.0/sum)
e30b
;
}

printf("\n******************************************************\n\n");
printf("下面输出输出ToBeTran.data及其报文Code.txt\n\n");
FILE *fp2,*fp3;
fp2=fopen("ToBeTran.txt","r");
fp3=fopen("Code.txt","r");

char ch;

printf("ToBeTran.data :\n");
do
{
ch=fgetc(fp2);
printf("%c",ch);
}while(ch!=EOF);
printf("\n\n\n");

printf("Code.txt :\n");
do
{
ch=fgetc(fp3);
printf("%c",ch);
}while(ch!=EOF);
printf("\n\n\n");

printf("\n******************************************************\n\n");
printf("下面输出CodeFile.data及其原文Textfile.txt\n\n");
FILE *fp4,*fp5;
fp4=fopen("CodeFile.txt","r");
fp5=fopen("Textfile.txt","r");

printf("CodeFile.data :\n");
do
{
ch=fgetc(fp4);
printf("%c",ch);
}while(ch!=EOF);
printf("\n\n\n");

printf("Textfile.txt :\n");
do
{
ch=fgetc(fp5);
printf("%c",ch);
}while(ch!=EOF);
printf("\n\n\n");
}

int main()
{
printf("\n******************************************************\n\n");
Initialzation();//初始化
EnCoding();//编码
printf("\n******************************************************\n\n");
Decoding();//译码
printf("\n******************************************************\n\n");
Output();//输出
system("pause");
return 0;
}


写入的内容

Code.txt:

100011011 100110111010 1111110101111 11000010111110 0110110010001000101001100110 1101110000 110001101111001 001011111110011010 11011010

000000001100101101010 100011011 0010110111011111010 110001101111001 00111011000001010 1111100110001 0000101000111101010000001010 11011001 1111100110001 11011010 001011110100110011 0001101101111010 100111111010110011110111100110011110001

01101100111000011000001011101101100100100 11010110 101001110010 01111101101101010 1000010110010110101110110000110010011 101001011110001011100101110 1011110000 00111101110000010

Textfile.txt:

do not aim for success if you want it

just do what you love and believe in and it will come naturally

suffering is the most powerful teacher of life
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: