首先定义哈夫曼树结构体:
typedef struct HTNode{
char ch;
int weight;
int parent,lchild,rchild;
int tag;//是否访问过的标志
char code[56];//哈夫曼码
int ceng;//表示层数
}HTNode,*HuffmanTree;
我们前26个数组存储小写字母,27-52存储大写字母,53-54存储,。空格?四个字符
所以一共有56种字符,那么建立哈夫曼树就要建立55个额外的节点,那么数组就要准备110个
首先准备英文文章,存在记事本里,在读取
1.英文文本:
Today I begin a new lifeToday I shed my old skin which hath, too long, suffered the bruises of failure and the wounds of mediority.Today I am born anew and my birthplace is a vineyard where there is fruit for all.Today I will pluck grapes of wisdom from the tallest and fullest vines in the vineyard,for these were planted by the wisest of my profession who have come before me,generation upon generation.Today I will savor the taste of grapes from these vines and verily I will swallow the seed of success buried in each and new life will sprout within me.The career I have chosen is laden with opportunity yet it is fraught with heartbreak and despair and the bodies of those who have failed, were they piled one atop another, would cast a shadow down upon all the pyramids of the earth.Yet I will not fail, as the others, for in my hands I now hold the charts which will guide through perilous waters to shores which only yesterday seemed but a dream.Failure no longer will be my payment for strug
文章的读取
int readFile(const char *fname, char *buffer, int bufferLen)//读取文件
{
int retLen=0;
FILE *f;
f = fopen(fname, "r"); //r=打开一个已有的文本文件,允许读取文件。
if(f){
retLen= fread(buffer, 1, bufferLen, f);
buffer[retLen]=0;
fclose(f);
}
if(retLen <= 0){
printf("读取文件错误,或者空文件!");
return 0;
}
return retLen;
}
先对各个成员初始化左右孩子下标初始化为-1,权重初始化为0,字符ch存储相应字符,权重weight初始化为0,哈夫曼码部分暂时存储'\0';
tag表示是否被访问过,后面的部分函数会用到;
统计文章中各个字符数:
int tongji(HTNode *a,char *b)//统计一篇英文文章各字符出现的个数
{
int j=0;
for(int i=0;i<26;i++)
f a[i].ch='a'+i;
or(int i=26;i<52;i++)
a[i].ch='A'+j++;
a[52].ch=',';
a[53].ch='.';
a[54].ch=' ';
a[55].ch='?';
for(int i=0;i<111;i++) //初始化
{
a[i].parent=0;
a[i].lchild=-1;
a[i].rchild=-1;
a[i].weight=0;
a[i].tag=0;
a[i].code[0]='\0';
a[i].ceng=0;
}
for(int i=0;i<1000;i++)
for(int j=0;j<56;j++)
if(b[i]==a[j].ch)
(a[j].weight)++;
}
返回权值最小节点下标函数(为后面创建哈夫曼树做准备):
int MinNode(HTNode *a)//返回权值最小节点的下标
{
int t;//储存权值最小节点的下标
for(int i=0;i<56+s;i++)
if(a[i].tag!=1)
t=i;
for(int i=0;i<56+s;i++)
if(a[i].tag!=1&&a[i].weight<a[t].weight)
t=i;
a[t].tag=1;
return t;
}
创建哈夫曼树(共56种字符,所以要在for循环种循环55次)‘#’表示建立哈夫曼树中生成的结点:
int CreatHfmTree(HTNode *a)//创建哈夫曼数
{
int i=56;
for(int j=0;j<55;j++)//一共需要调用55次构建哈夫曼树函数
{
int min1,min2;
min1=MinNode(a);
min2=MinNode(a);
a[i+j].ch='#';
a[i+j].weight=(a[min1].weight+a[min2].weight);
a[i+j].lchild=min1;
a[i+j].rchild=min2;
a[min1].parent=i+j;
a[min2].parent=i+j;
s++;
}
}
建立好哈夫曼树后,进行哈夫曼编码。从从根向叶子节点遍历比较困难,所以我们从叶子节点向根寻找,把他暂时存储到temp字符数组中。
再依次把temp字符数组中的字符串逆序存储到哈夫曼数结构体中的code即可再依次把temp字符数组中的字符串逆序存储到哈夫曼数结构体中的code即可
int HfmCode(HTNode *a)//编码
{
int i=0,j=0;
int next,now;//now记录当前节点的下标。next记录parent节点的下标
char temp[56];
HTNode *p;
for(i=0;i<56;i++)
{
j=0;
p=&a[i];
next=p->parent;
now=i;
while(now!=110)
{
if(a[next].lchild==now)
temp[j++]='0';
else
temp[j++]='1';
now=next;
next=a[next].parent;
}
int g=j-1;
int k;
for(k=0;k<j;k++)
{
a[i].code[k]=temp[g];
g--;
}
a[i].code[k]='\0';
}
}
这时候我们在主函数中通过for循环依次输出数组下标,存储字符,权重,双亲节点下标,左右孩子节点下标,以及哈夫曼码:我这里手动算了一下全部节点的和为1000;说明算对了
最后就是把文章翻译成哈夫曼编码(for循环一一对应就好,一个一个来,我这里没考虑时间复杂度,我这个函数的时间复杂度度应该非常高):
int CoutCode(HTNode *a,char p[1000])//显码
{
char *q=p;
while(*q!='\0')
{
for(int i=0;i<56;i++)
if(a[i].ch==*q)
printf("%s",a[i].code);
q++;
}
}
在主函数中验证打印
哈哈有点看不清是不是
这里我实际做了一下验证
第一个单词Today的哈夫曼码应该为:
0000101101011000100100000
实际为:
0000101101011000100100000
运行正确
下面就是全部代码了
我这里还多了一个打印树形的部分,刚接触数据结构,想了好几天一没想到好的办法,所以我就用层次遍历看了一下
我就手动把前八层树动输出了。。。。。。。后面的28层用了一个for循环实现了(ps:有会的可以教下我顺序树的显示树形,这个下标套来套去我就晕了。。。)
层次遍历
int rechar=130;//后28行处理
for(int i=15;i<64;i=i+2)
{
for(int j=rechar-1;j<rechar+2;j++)
ch[i+1][j]='-';
ch[i+2][rechar-1]=a[child[c++]].ch;
ch[i+2][rechar+1]=a[child[c++]].ch;
rechar--;
}
好了下面给出全部代码
#include<stdio.h>
#include<iostream>
#include<queue>
using namespace std;
typedef struct HTNode{
char ch;
int weight;
int parent,lchild,rchild;
int tag;//是否访问过的标志
char code[56];//哈夫曼码
int ceng;//表示层数
}HTNode,*HuffmanTree;
int tongji(HTNode *a,char *b)//统计一篇英文文章各字符出现的个数
{
int j=0;
for(int i=0;i<26;i++)
a[i].ch='a'+i;
for(int i=26;i<52;i++)
a[i].ch='A'+j++;
a[52].ch=',';
a[53].ch='.';
a[54].ch=' ';
a[55].ch='?';
for(int i=0;i<111;i++) {
a[i].parent=0;
a[i].lchild=-1;
a[i].rchild=-1;
a[i].weight=0;
a[i].tag=0;
a[i].code[0]='\0';
a[i].ceng=0;
}
for(int i=0;i<1000;i++)
for(int j=0;j<56;j++)
if(b[i]==a[j].ch)
(a[j].weight)++;
}
int s=0;
int MinNode(HTNode *a)//返回权值最小节点的下标
{
int t;//储存权值最小节点的下标
for(int i=0;i<56+s;i++)
if(a[i].tag!=1)
t=i;
for(int i=0;i<56+s;i++)
if(a[i].tag!=1&&a[i].weight<a[t].weight)
t=i;
a[t].tag=1;
return t;
}
int CreatHfmTree(HTNode *a)//创建哈夫曼数
{
int i=56;
for(int j=0;j<55;j++)//一共需要调用55次构建哈夫曼树函数
{
int min1,min2;
min1=MinNode(a);
min2=MinNode(a);
a[i+j].ch='#';
a[i+j].weight=(a[min1].weight+a[min2].weight);
a[i+j].lchild=min1;
a[i+j].rchild=min2;
a[min1].parent=i+j;
a[min2].parent=i+j;
s++;
}
}
int HfmCode(HTNode *a)//编码
{
int i=0,j=0;
int next,now;//now记录当前节点的下标。next记录parent节点的下标
char temp[56];
HTNode *p;
for(i=0;i<56;i++)
{
j=0;
p=&a[i];
next=p->parent;
now=i;
while(now!=110)
{
if(a[next].lchild==now)
temp[j++]='0';
else
temp[j++]='1';
now=next;
next=a[next].parent;
}
int g=j-1;
int k;
for(k=0;k<j;k++)
{
a[i].code[k]=temp[g];
g--;
}
a[i].code[k]='\0';
}
}
int CoutCode(HTNode *a,char p[1000])//显码
{
char *q=p;
while(*q!='\0')
{
for(int i=0;i<56;i++)
if(a[i].ch==*q)
printf("%s",a[i].code);
q++;
}
}
int addceng(HTNode *a)//对每一个节点的层数赋值
{
a[110].ceng=1;
int i=1,j;
int p;
for(i;i<50;i++)
for(j=109;j>=0;j--)
{
p=a[j].parent;
if(a[p].ceng==i)
{
a[j].ceng=i+1;
a[j].tag=1;
}
}
}
int child[110]={110,108,109,104,105,106,107,78,4,99,100,101,102,103,54,93,18,17,7,8,19,94,0,14,95,96,97,24,89,5,22,90,91,3,11,92,13,85,2,86,12,15,87,20,88,84,45,53,52,21,34,1,6,83,10,82,50,81,31,80,51,79,49,78,48,77,47,76,46,75,44,74,43,73,42,72,41,71,40,70,39,69,38,68,37,67,36,66,35,65,33,64,32,63,30,62,29,61,28,60,27,59,26,58,25,57,23,56,16};
int path[110]={70,131,55,86,115,146,48,63,78,93,108,123,138,153,45,52,75,82,89,96,105,112,119,126,135,142,44,47,104,107,123,128,134,137,140,143,45,48,122,125,126,129,139,142};
int roof[110]={100,70,130,55,85,115,145,48,62,78,92,108,122,138,152,45,51,75,81,89,95,105,111,119,44,46,104,106,123,127,134,45,47,122,124,126,128};
int leverorder(HTNode *a)//层次遍历 (并且在左边的放在左边输出,在右边的放在右边输出,按照次序)
{
int i=1,j,k;
for(i;i<40;i++)
{
for(int l=0;l<120-3*i;l++)
printf(" ");
int num=1;
for(j=0;j<111;j++)
for(j=0;j<111;j++)
{
if(a[j].ceng==i)
printf("%c ",a[j].ch);
}
num++;
for(k=0;k<5-2*i;k++)
printf(" ");
cout<<endl;
}
}
void CoutHTNode(HTNode *a)//输出树形
{
char ch[64][201];
for(int i=0;i<64;i++)
for(int j=0;j<201;j++)
ch[i][j]=' ';
int c=0;//c,使用chlid中第几个数。
int p=0,r=0;//path数字的行号(“-----”),roof的行号
ch[0][100]=a[child[c++]].ch;//前8行处理,手动打印....
for(int i=70;i<131;i++)ch[1][i]='-';
ch[2][70]=a[child[c++]].ch;ch[2][130]=a[child[c++]].ch;
for(int i=55;i<86;i++)ch[3][i]='-';for(int i=115;i<146;i++)ch[3][i]='-';
ch[4][55]=a[child[c++]].ch;ch[4][85]=a[child[c++]].ch;ch[4][115]=a[child[c++]].ch;ch[4][145]=a[child[c++]].ch;
for(int i=48;i<63;i++)ch[5][i]='-';for(int i=78;i<93;i++)ch[5][i]='-';
for(int i=108;i<123;i++)ch[5][i]='-';for(int i=138;i<153;i++)ch[5][i]='-';
ch[6][48]=a[child[c++]].ch;ch[6][62]=a[child[c++]].ch;ch[6][78]=a[child[c++]].ch;ch[6][92]=a[child[c++]].ch;
ch[6][108]=a[child[c++]].ch;ch[6][122]=a[child[c++]].ch;ch[6][138]=a[child[c++]].ch;ch[6][152]=a[child[c++]].ch;
for(int i=45;i<52;i++)ch[7][i]='-';for(int i=75;i<82;i++)ch[7][i]='-';for(int i=89;i<96;i++)ch[7][i]='-';
for(int i=105;i<112;i++)ch[7][i]='-';for(int i=119;i<126;i++)ch[7][i]='-';for(int i=135;i<142;i++)ch[7][i]='-';
ch[8][45]=a[child[c++]].ch;ch[8][51]=a[child[c++]].ch;ch[8][75]=a[child[c++]].ch;ch[8][81]=a[child[c++]].ch;ch[8][89]=a[child[c++]].ch;
ch[8][95]=a[child[c++]].ch;ch[8][105]=a[child[c++]].ch;ch[8][111]=a[child[c++]].ch;ch[8][119]=a[child[c++]].ch;
ch[8][125]=a[child[c++]].ch;ch[8][135]=a[child[c++]].ch;ch[8][141]=a[child[c++]].ch;
for(int i=44;i<47;i++)ch[9][i]='-';for(int i=104;i<107;i++)ch[9][i]='-';for(int i=123;i<128;i++)ch[9][i]='-';
for(int i=134;i<137;i++)ch[9][i]='-';for(int i=140;i<143;i++)ch[9][i]='-';
ch[10][44]=a[child[c++]].ch;ch[10][46]=a[child[c++]].ch;ch[10][104]=a[child[c++]].ch;ch[10][106]=a[child[c++]].ch;
ch[10][123]=a[child[c++]].ch;ch[10][127]=a[child[c++]].ch;ch[10][134]=a[child[c++]].ch;
ch[10][136]=a[child[c++]].ch;ch[10][140]=a[child[c++]].ch;ch[10][142]=a[child[c++]].ch;
for(int i=45;i<48;i++)ch[11][i]='-';for(int i=122;i<125;i++)ch[11][i]='-';
for(int i=126;i<129;i++)ch[11][i]='-';for(int i=139;i<142;i++)ch[11][i]='-';
ch[12][45]=a[child[c++]].ch;ch[12][47]=a[child[c++]].ch;ch[12][122]=a[child[c++]].ch;
ch[12][124]=a[child[c++]].ch;ch[12][126]=a[child[c++]].ch;ch[12][128]=a[child[c++]].ch;
ch[12][139]=a[child[c++]].ch;ch[12][141]=a[child[c++]].ch;
for(int i=44;i<47;i++)ch[13][i]='-';for(int i=121;i<124;i++)ch[13][i]='-';for(int i=127;i<130;i++)ch[13][i]='-';
for(int i=140;i<143;i++)ch[13][i]='-';
ch[14][44]=a[child[c++]].ch;ch[14][46]=a[child[c++]].ch;ch[14][121]=a[child[c++]].ch;
ch[14][123]=a[child[c++]].ch;ch[14][127]=a[child[c++]].ch;ch[14][129]=a[child[c++]].ch;
ch[14][140]=a[child[c++]].ch;ch[14][142]=a[child[c++]].ch;
ch[15][44]='+';
for(int i=45;i<130;i++)ch[15][i]='-';ch[15][130]='+';
int rechar=130;//后28行处理
for(int i=15;i<64;i=i+2)
{
for(int j=rechar-1;j<rechar+2;j++)
ch[i+1][j]='-';
ch[i+2][rechar-1]=a[child[c++]].ch;
ch[i+2][rechar+1]=a[child[c++]].ch;
rechar--;
}
for(int i=0;i<64;i++)
{
for(int l=0;l<201;l++)
printf("%c",ch[i][l]);
cout<<endl;
}
}
int readFile(const char *fname, char *buffer, int bufferLen)//读取文件
{
int retLen=0;
FILE *f;
f = fopen(fname, "r"); //r=打开一个已有的文本文件,允许读取文件。
if(f){
retLen= fread(buffer, 1, bufferLen, f);
buffer[retLen]=0;
fclose(f);
}
if(retLen <= 0){
printf("读取文件错误,或者空文件!");
return 0;
}
return retLen;
}
int main()
{
char buf[1000];
const char fname[]= "D:/数据结构cpp文件/徐嘉辉 2020202527 20计科05 第二次实验/yingwen.txt";
int retLen= readFile(fname,buf,1000);
if(retLen != 0) {
printf("读取的数据是: %s \n",buf);
}
cout<<endl;
printf("建立56个数组,前26储存小写字母,后26储存大写字母,最后4个空间储存,.空格?四个符号\n");
HTNode num[111];//56+55 n个数据2n-1个节点
tongji(num,buf);
cout<<endl;
CreatHfmTree(num);//建立哈夫曼树
HfmCode(num);
cout<<"按照数组下标,储存的字符,权重,双亲,左孩子,右孩子,哈夫曼码的顺序输出"<<endl;
cout<<"下标 "<<"字符 "<<"权重 "<<"双亲下标"<<" 左孩子下标 "<<" 右孩子下标"<<" 哈夫曼码"<<endl;
for(int i=0;i<111;i++)
{
int f=0;
printf("%4d %4c %4d %4d %10d %10d ",i,num[i].ch,num[i].weight,num[i].parent,num[i].lchild,num[i].rchild);
while(num[i].code[f]!='\0')
putchar(num[i].code[f++]);
cout<<endl;
}
for(int i=0;i<111;i++)
num[i].tag=0;
cout<<endl;
cout<<"整篇文章的哈夫曼编码为:"<<endl;
CoutCode(num,buf);
cout<<endl;
addceng(num);
cout<<endl<<"每个节点的层数为:"<<endl;
for(int i=0;i<111;i++)
printf("%d ",num[i].ceng);
cout<<endl<<"层次遍历:"<<endl;
leverorder(num);
cout<<endl;
CoutHTNode(num);
}
整个函数的运行结果:
欢迎赐教