您的位置:首页 > 理论基础 > 数据结构算法

数据结构:哈夫曼树,哈夫曼编码与译码系统

2016-02-13 20:09 537 查看
1).结构体类型 HaffmanfT:

typedefstruct{

int weight;

char ch;

char chh[20];

int lchild;

int rchild;

int parent;

}HaffmanT;/*结点结构体*/

2). (字母部分用的结构体)结构体类型 HaffmanfN

typedefstruct

{

char ch;

char bits[MB + 1];

int start;//标志编码起始位

}HaffmanN;/*保存编码*/

3 ).单词部分的储存编码和权值用了三个数组(全局变量):

charstr2[MB][20];//单词字串

charstr3[MB][20];//输入串

intstrC[MB];//单词字串计数数组.对应每个单词串,拥有一个计算器

4). (全局变量)charmi[MAXPLUS];//存放的是字符串编码

charcha[MAXPLUS];//存放的是句子

2.

实验所用到的全部函数有:

1).voidstrCount();//统计字母并计算每个字母出现的次数即权值

2).intwordCount();//统计单词并计算每个单词出现的次数即权值,返回出现的不同单词个数

3).void init(intn);//初始化

4).voidgetHaff(int n,int p);//把字母(单词)及权值保存到结构体中,n决定单词还是字母,p决定了结构体数组的大小

5).int creat2(int n);//创建哈夫曼树

6).voidCharSetHuffmanEncoding(int n,int wh);//给每一个结点编码,n代表结点个数

7).void bianma(intwh,int p);//给从文件中读到的文章整体编码,即压缩

8).void wriIn();//将对文件的编码写入新的TXT文档

9).void yima(intn,int wh); //将哈夫曼编码解码为原文档

10).int main();//综合调用并计算压缩率

详细代码如下:

//#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "io.h"

#define MB 300//最大编码的值
#define MAXPLUS 100000
typedef struct{
int weight;
char ch;
char chh[20];
int lchild;
int rchild;
int parent;

}HaffmanT;/*结点结构体*/
HaffmanT T[MB];
HaffmanT TW[MB];
typedef struct
{
char ch;
char bits[MB + 1];
int start;//标志编码起始位
}HaffmanN;
HaffmanN N[MB];

char str2[MB][20];//单词字串结果存放数组
char str3[MB][20];//输入串或者用char *str2也可,这样可以
int strC[MB];//单词字串计数数组.对应每个单词串,拥有一个计算器
char mi[MAXPLUS];//存放的是字符串编码
char cha[MAXPLUS];//存放的是句子

void strCount();//统计字母并计算每个字母出现的次数即权值
int wordCount();//统计单词并计算每个单词出现的次数即权值,返回出现的不同单词个数
void init(int n);//初始化
void getHaff(int n,int p);//把字母(单词)及权值保存到结构体中,n决定单词还是字母,p决定了结构体数组的大小
int creat2(int n);//创建哈夫曼树
void CharSetHuffmanEncoding(int n,int wh);//给每一个结点编码,n代表结点个数
void bianma(int wh,int p);//给从文件中读到的文章整体编码,即压缩
void wriIn();//将对文件的编码写入新的TXT文档
void yima(int n,int wh);  //将哈夫曼编码解码为原文档
int main()
{
int t,k,cho;
strCount();
k=wordCount();
int n = 29;
printf("请选择编码:0,单词,1字母\n");
scanf("%d",&cho);
if(cho==0)
{
printf("你输入了”0“,将给出单词的频率及相应的哈夫曼编码\n");
getHaff(cho,k);
creat2(k);
CharSetHuffmanEncoding(k-1,cho);//k-1
printf("是否压缩:1:是,0:否:\n");
scanf("%d",&cho);
if(cho==0)
return 0;
if(cho==1)
{

printf("接下来是压缩环节\n");
bianma(cho,k);
wriIn();

}
printf("是否解压缩:1:是,0:否:\n");
scanf("%d",&cho);
if(cho==0)
return 0;
if(cho==1)
{
yima(k,cho);
}
printf("\n");
printf("计算压缩率:\n");
double ee=(double)strlen(mi)/(strlen(cha)*8);
ee=1-ee;
printf("压缩率为%.2f",ee*100);
printf("%%");

}
if(cho==1)
{
printf("你输入了”1“,将给出字符的频率及相应的哈夫曼编码\n");
getHaff(cho,n);
creat2(n);
CharSetHuffmanEncoding(n,cho);
printf("是否压缩:1:是,0:否:\n");
scanf("%d",&cho);
if(cho==0)
return 0;
if(cho==1)
{

printf("接下来是压缩环节\n");
bianma(cho,n);
wriIn();

}
printf("是否解压缩:1:是,0:否:\n");
scanf("%d",&cho);
if(cho==0)
return 0;
if(cho==1)
{
yima(n,cho);
}
printf("\n");
printf("计算压缩率:\n");
double ee=(double)strlen(mi)/(strlen(cha)*8);
ee=1-ee;
printf("压缩率为%.2f",ee*100);
printf("%%");
}

}

//先是统计字符的功能
void strCount(){

FILE * fp;
char str1;
if((fp=fopen("F:\\untitled\\DataStructure\\实验\\实验二\\aaa.txt","r"))==NULL)
{
printf("file cannot be opened\n");
exit(1);
}
int x;//数组下标
for(x=0;x<29;x++)
{
TW[x].ch='a'+x;
TW[x].weight=0;
// printf("%c",N[x].ch);
}
TW[26].ch=' ';
TW[27].ch=',';
TW[28].ch='.';
x=0;

while((str1=fgetc(fp))!=EOF)
{
//cha[x]+=str1;

if(isalpha(str1))
{
str1=tolower(str1);
++TW[str1-'a'].weight;//直接用数组的ascii做下标
}
else
if((int)str1==32)
++TW[26].weight;//这个是空格
else if((int)str1==44)
++TW[27].weight;
else if((int)str1==46)
++TW[28].weight;

cha[x]=str1;
x++;
}
//fgets(cha,10000,fp);
printf("我的句子是:%s\n",&cha);

fclose(fp);
}

//统计单词
int wordCount()
{

int i=0,j=0,k=0,x,y=0;

while(j<strlen(cha)+1)

{
for(;cha[j]==32||cha[j]==44||cha[j]==46;j++);

while(k<20&&cha[j]!=32&&cha[j]!=44&cha[j]!=46)
str2[i][k++]=cha[j++];
str3[y][k++]=cha[j++];
str2[i][k]='\0';
strC[i]=1;
// strcpy(str3[j],str2[j]);
y++;
for(x=0;x<i;x++)
if(strncmp(str2[i],str2[x],20)==0)
{
strC[x]++;

i--;
break;
}
i++;
k=0;
//printf("i是%d,值是%s\n",i,str2[i]);
}
str2[i+1][0]=' ';
str2[i+2][0]=',';
str2[i+3][0]='.';

return i;
}
//创建树之前赶紧把权值给搞好
void getHaff(int n,int p)//n是操作符,p是数组大小
{
int i=n;
int k=p;
int c=0;
if(i==1)
{
for(;c<p;c++){
T[c].weight=TW[c].weight;
T[c].ch=TW[c].ch;}
}

if(i==0)
{
for(c=0;c<p-1;c++)
T[c].weight=strC[c];
//  strcpy(T[c].chh,str2[c]);
// printf("%s,不输出啊吗",T[c].chh);
}
}

//接下来构造一棵哈夫曼树
void init(int n)
{

int i;
for (i = 0; i<2 * n - 1; i++)
{
//T[i].weight = 0;//权值
T[i].parent = -1;
T[i].lchild = -1;
T[i].rchild = -1;
}

}

int creat2(int n)
{
int i, j, p1, p2, w1, w2;//p1,p2分别是最小权值的位置,w1,w2代表最小权值
//初始化结点
int num = n;
init(n);
//循环构造哈夫曼树
for (i = 0; i<num - 1; i++)
{
w1 = w2 = 10000;
p1 = p2 = 0;
for (j = 0; j<num + i; j++)//循环条件
{
if (T[j].weight<w1&&T[j].parent == -1)
{
w2 = w1;
p2 = p1;
w1 = T[j].weight;
p1 = j;

}
else if (T[j].weight<w2&&T[j].parent == -1)
{
w2 = T[j].weight;
p2 = j;
}
}//找到了p1,p2两个最小的位置
//printf("%d %d我要看看p的值!\n",p1,p2);
T[p1].parent = T[p2].parent = num + i;    //找到当前最小的两个节点 确定父节点
T[num + i].lchild = p1;                   //设置父节点的左右子树和权值
T[num + i].rchild = p2;
T[num + i].weight = T[p1].weight + T[p2].weight;//特别注意一下是num+i
}

return i;
}

void CharSetHuffmanEncoding(int n,int wh)//n表示多少个节点
{
//根据树求编码表
//int n=init();
int c, p, i, j;
HaffmanN cd;//临时变量来存放求解编码时的信息
for (i = 0; i < n; i++)
{
cd.start = n - 1;
c = i;

while ((p = T[c].parent) != -1)   /* 父结点存在 */
{
cd.bits[cd.start] = (T[p].lchild == c) ? '0' : '1';
cd.start--;        /* 求编码的低一位 */
c = p;
} /* end while */
/* 保存求出的每个叶结点的哈夫曼编码和编码的起始位 */
for (j = cd.start + 1; j<n; j++)
N[i].bits[j] = cd.bits[j];
N[i].start = cd.start;
} /* end for */

/* 输出已保存好的所有存在编码的哈夫曼编码 */
int n1; char temp;
for (i = 0; i<n; i++)
{
if(wh==1){
N[i].ch = T[i].ch;
printf("%c 出现的次数是%d,它的编码是: ", T[i].ch,T[i].weight);}
//for (n=0)
if(wh==0)
printf("第%d项,%s 出现的次数是%d,它的编码是: ", i,str2[i],T[i].weight);
for (j = N[i].start + 1,n1=0; j < n; j++,n1++)
{
temp = N[i].bits[j];
N[i].bits[n1] = temp;
//	printf("%c对比", N[i].bits[j]);
printf("%c", N[i].bits[n1]);
}
//printf(" start:%d", N[i].start);

printf("\n");

}

}
//接下来是给字符串编码
void bianma(int wh,int p)
{
int n1=strlen(cha);
int n2=strlen(str3);
int i,j;
if(wh==1)
{

for(i=0;i<=n1;i++)
{

for(j=0;j<29;j++)
{

if(cha[i]==N[j].ch)
{
//mi[i].bits=gets(N[j].bits);
printf("%s",N[j].bits);
strcat (mi,N[j].bits);
break;
}
}
}
}else if(wh==0)
{
for(i=0;i<=n2;i++)
{

for(j=0;j<p;j++)
{

if(strncmp(str3[i],T[j].chh,20)==0)
{
//mi[i].bits=gets(N[j].bits);
printf("%s",N[j].bits);
strcat (mi,N[j].bits);
break;
}
}
}
}
printf("\n");

}
//然后把字符串的码写到文件中
void wriIn()
{
FILE *pFile = fopen("F:\\untitled\\DataStructure\\实验\\实验二\\aab.txt", //打开文件的名称
"w"); // 文件打开方式 如果原来有内容也会销毁
//向文件写数据
fwrite (mi, //要输入的文字
1,//文字每一项的大小
strlen(mi),//单元个数
pFile //我们刚刚获得到的地址
);
// printf("测试下mi的值%c:\n",mi[0]);

fflush(pFile);
}

//译码的实现
void yima(int n,int wh) {

int m = 2*n - 2;//原来是这里!!!!!!!!!!
int temp = m;
int i = 0;
printf("译码:\n");
/*char str[MAXPLUS];
gets(str);*/

//printf("weisheme ");
//char str[3]={'0','1'};
//for ()
while (mi[i] != '\0') {
//挨个读入电文
//printf("看一下有没有出粗哦%c第几位%d\n", str[i], i);
if (mi[i] == '0')
//如果是0进入左子树   如果是1进入右子树
{
//	printf("is0do");
temp = T[temp].lchild;
//printf("现在我的值是:%c\n", T[temp].ch);
}
else if (mi[i] == '1')
temp = T[temp].rchild;
if (T[temp].lchild == -1 && T[temp].rchild == -1) {
if(wh==1)//如果该节点左右均为空 即到达字母结点 输出
printf("%c", T[temp].ch);
else if(wh==0)
printf("%s", T[temp].chh);
temp = m;                                               //重新回到根节点 进行下一字符的译码
}
i++;
}

getchar();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: