哈夫曼树的实现,编码以及译码

2. 编程实现如下功能:

(1)建立由英文字符组成的文件f1(字符种类≥10,长度≥100)并统计不同字符出现的次数;

(2)按字符出现的次数对其建立哈夫曼树,并求出各个字符的哈夫曼编码;

(3)读入要编码的文件f1,编码后存入另一个文件f2

4)接着再调出编码后的文件f2,对其进行译码输出,最后存入文件f3。

#include<iostream>
#include<fstream>
#include<cstring>
using namespace std;

#define NODE 51    //节点数:26个字母,共需26*2-1=51个结点
#define ZI 26       //26个字母

typedef struct Huffman{     //Huffmantree结构
    int weight;
    int parent,lchild,rchild;
}Huffman,*HuffmanTree;

typedef char **HuffmanCode; //动态分配数组存储哈夫曼编码表

void Select(HuffmanTree T,int len,int &s1,int &s2){
    int i,min1=100000,min2=100000;    //初始化为第一个的值找出最小的权重值然后组合成新的树
    for(i=1;i<=len;i++){        //这里字母的数量最小值都大于100000这个min就存在问题了(之后可以优化)
        if(T[i].weight<min1 && T[i].parent==0){
            min1=T[i].weight;
            s1=i;   
        }       //这里通过遍历找出第一个最小权重值
    }

    int temp=T[s1].weight;
    T[s1].weight=100000;    //这里为了找出第二个最小权重值,必须把第一个最小值临时存入temp,
                            //然后将它设为足够大才能保证不会重复使用,但其实和上面问题一样(可以优化)
    for(i=1;i<=len;i++){
        if (T[i].weight < min2 && T[i].parent == 0){
            min2 = T[i].weight;
            s2 = i;             //找第二个最小值
        }
    }
    T[s1].weight = temp; //恢复原来的值
}

void Create(HuffmanTree &T,int word[]){     //创建哈夫曼树,叶子结点26个,将文件中的字符统计数组计入树中
    int i,j,s1,s2;
    T=new Huffman[NODE+1];  
    for(i=1;i<=NODE;i++){
        T[i].parent=0;
        T[i].lchild=0;
        T[i].rchild=0;
    }
    for(j=1;j<=26;j++)
        T[j].weight=word[j];

/*--------------------  以上皆为初始化  ---------------------*/

    for(i=ZI+1;i<=NODE;i++){
        Select(T,i-1,s1,s2);    //找俩权重最小值(算法可优化)
        T[s1].parent=i;
        T[s2].parent=i;
        T[i].lchild=s1;
        T[i].rchild=s2;
        T[i].weight=T[s1].weight+T[s2].weight;
    }

}

void CreateHuffmancode(HuffmanTree T,HuffmanCode &HC){
    int start;
    HC=new char*[ZI+1];      //分配编码空间
    char *cd=new char[ZI];    //因为哈夫曼编码不可能大于26
    cd[ZI-1]='\0';   //编码结束符
    for(int i=1;i<=ZI;i++){ //为每个字符编码
        start=ZI-1; //这里start要在HC的最后面,也就是结束符的位置
        int c=i;
        int f=T[i].parent;
        while(f!=0){
            start--;
            if(T[f].lchild==c)
                cd[start]='0';
            else
                cd[start]='1';
            c=f;
            f=T[f].parent;
        }
        HC[i]=new char[ZI-start];
        strcpy(HC[i],&cd[start]);   
    }
        delete cd;
}



int main(){
    int word[27]={0};     //建立数组存储26字母个数作为哈夫曼树权值
    char c;

    FILE *fp=fopen("f1.txt","r");
    if(fp){
        while((c=getc(fp))!=EOF){
            int size=(int)c; //这里针对26字母的ASCII值将不同字母以1-26
            if(size>=65 && size<=90)    //大写化为小写
                size=size+32;
            if(size>=97 && size<=122){  //方便归一到数组
            size=size-96;
            word[size]++; 
            }      //代入到word数组中方便后续统计
        }
    } 
    fclose(fp);
    
    HuffmanCode HC;
    HuffmanTree T;
    Create(T,word);     //创建哈夫曼树
    CreateHuffmancode(T,HC);    //哈夫曼编码

/*  下面这一段我调试了很久,无论怎么试都无法将字符打印到文件中,
    后来修改发现,我如果重新创建一个FILE类型的文件对f1.txt重新
    进行读取就可以打印出来,测试后发现是因为while((c=getc(fp3))!=EOF){ 
    这里的代码为EOF,具体原因我猜想是getc函数具有记忆性,相当于它上次执行
    以及读取到了最后面,再次读取时就会直接EOF   */
    
 /*-------------------------  编码  ----------------------------*/   
    FILE *fp3=fopen("f1.txt","r");  
    FILE *fp2=fopen("f2.txt","a");  
    if(fp3 && fp2){
        while((c=getc(fp3))!=EOF){   
            int size=(int)c; 
            if(size>=65 && size<=90)   //大小写转换
                size=size+32;
            if(size>=97 && size<=122){  
            size=size-96;
            fputs(HC[size],fp2);    
            fputs("\n",fp2);    //这里添加原因是因为vscode警告一行的数字太多
                                // 所以我就写入了一个换行符
    }
            if(size==32)
                fputs("2",fp2);     //这里是后续添加,因为发现译码时没有空格
                                    //英文没有空格符,没有可识度,于是用2表示空格
    }
}
    fclose(fp2);
    fclose(fp3);    //这里防止又出现以上情况,这里直接close重新创建

/*------------------------  译码  --------------------------*/
    FILE *fp4=fopen("f2.txt","r");  
    FILE *fp5=fopen("f3.txt","a"); 
    if(fp4 && fp5){
        int h=NODE;
        while((c=getc(fp4))!=EOF){ 
            int ts=(int)c-48;           //这里同样调试很久发现无法译码是因为getc返回的为char类型
            if(ts==2)                   // 即便强制转换也会变为它对应的ASCII值,所以进行了转换
                fputs("\n",fp5);
            if(T[h].lchild==0 && T[h].rchild==0){
                char ls=(char)(h+96);
                fputc(ls,fp5);
                h=NODE;
            }
            if(ts==1)
                h=T[h].rchild;
            if(ts==0)
                h=T[h].lchild;
        }
    } 

    fclose(fp4);
    fclose(fp5);
    
    return 0;

}

f1.txt 随便找一段英文短句即可。

HAUT的宝贝们不要抄作业

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值