数据结构课程设计-(三)哈夫曼编码器

哈夫曼编/译码器
任务:建立最优二叉树函数。
要求:可以建立函数输入二叉树,并输出其哈夫曼树。
在上交资料中请写明:存储结构、基本算法(可以使用程序流程图)、输入输出、源程序、测试数据和结果、算法的时间复杂度、另外可以提出算法的改进方法;
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站写一个哈夫曼码的编/译码系统。
一个完整的系统应具有以下功能:
(1)I:初始化(Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。
(2)E:编码(Encoding)。利用已建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。
(3)D:译码(Decoding)。利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。
(4)P:印代码文件(Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件CodePrin中。
(5)T:印哈夫曼树(Tree printing)。将已在内存中的哈夫曼树以直观的方式(树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint中。

huffman树的数据结构定义如下:

typedef struct
{
    int weight;
    int lchild,rchild,parent;
} HTNode,*HuffmanTree;

(一)哈夫曼树的建立
1.从所给数据中选择两个最小的值

void Select(HuffmanTree HT,int n,int &s1,int &s2)
{
    int i=1;
    while(HT[i].parent!=0&&i<=n)//找到一个还没连接的,即parent=0;
        i++;
    if(i==n+1)
        return ;
    s1=i;
    i++;
    while(HT[i].parent!=0&&i<=n)//同上
        i++;
    if(i==n+1)
        return ;
    s2=i;
    i++;
    if(HT[s1].weight>HT[s2].weight)//让s1比s2小
        swap(s1,s2);
    for(; i<=n; i++)
    {
        if(HT[i].parent==0)
        {
            if(HT[i].weight<HT[s1].weight)
                s2=s1,s1=i;
            else if(HT[i].weight<HT[s2].weight)
                s2=i;
        }
    }
    return ;
}

2.根据输入的字符和权值建立哈夫曼树

void CreatHuffmanTree(HuffmanTree &HT,int n)
{
    if(n<=1)
        return ;
    int m=2*n-1;//树共有2n-1个节点
    HT=(HuffmanTree)malloc(sizeof(HTNode)*(m+1));//从1开始多申请一个
    for(int i=1; i<=m; i++)//初始化
    {
        HT[i].lchild=0;
        HT[i].parent=0;
        HT[i].rchild=0;
        HT[i].weight=0;
    }
    for(int i=1; i<=n; i++)
        cin>>HT[i].weight;
    int s1,s2;
    for(int i=n+1; i<=m; i++)//后n-1个节点的创建
    {
        Select(HT,i-1,s1,s2);//新节点的权值为两个最小的权值和
        HT[s1].parent=i;
        HT[s2].parent=i;
        HT[i].lchild=s1;
        HT[i].rchild=s2;
        HT[i].weight=HT[s1].weight+HT[s2].weight;
    }
    return ;
}

(二)根据哈夫曼树对字符进行编码

void CreatHuffmanCode(HuffmanTree &HT,char ** &HC,int n)
{
    char *col;
    HC=(char **)malloc(sizeof(char *)*(n+1));
    col=(char *)malloc(sizeof(char)*n);//存储每个编码的临时空间
    col[n-1]='\0';
    //为什么是n-1,n个节点创建的哈夫曼树高为n-1
    //路径条数为n-2,最长的只有n-2个数字。
    for(int i=1; i<=n; i++)
    {
        int str=n-1;
        int p=i,f=i;
        while(HT[f].parent!=0)//从叶向上寻找
        {
            f=HT[f].parent;
            if(HT[f].lchild==p)
                col[--str]='0';
            else if(HT[f].rchild==p)
                col[--str]='1';
            p=f;
        }
        HC[i]=(char *)malloc(sizeof(char)*(n-str));    //为第i个字符编码分配空间
        strcpy(HC[i],&col[str]);//复制到编码中
    }
    free(col);
    return ;
}

(三)翻译文件中的码

void TransCode(HuffmanTree HT,char b[],char a[],char c[],int n)
{
    //b数组是要翻译的二进制编码
    //a数组是叶子对应的字符
    //c数组存储翻译得到的内容
    FILE *fw;
    int q=2*n-1;   //初始化为根结点的下标
    int k=0;
    int len=strlen(b);
    if((fw=fopen("textfile.txt","w"))==NULL)
        cout<<"Open file error!"<<endl;
    for(int i=0; i<len; i++)
    {
        if(b[i]=='0')
            q=HT[q].lchild;
        else if(b[i]=='1')
            q=HT[q].rchild;
        if(HT[q].lchild==0&&HT[q].rchild==0)//叶子节点,此时可译
        {
            c[k++]=a[q];
            fputc(a[q],fw);
            q=2*n-1;
        }
        c[k]='\0';
    }
    return ;
}

(四)对文件中的字符进行编码

void Coding(HuffmanTree &HT,char ** &HC,int n,char a[])
{
    FILE *fp,*fw;
    char c;
    int k;
    if((fp=fopen("tobetran.txt","r"))==NULL)
        cout<<"Open file error!"<<endl;
    if((fw=fopen("codefile.txt","w"))==NULL)
        cout<<"Open file error!"<<endl;
    fscanf(fp,"%c",&c);//从文件中读取一个字符
    while(!feof(fp))
    {
        for(int i=1; i<=n; i++)
            if(a[i]==c)//找到对应的编码
            {
                k=i;
                break;
            }
        for(int i=0; HC[k][i]!='\0'; i++)//输出该字符编码
            fputc(HC[k][i],fw);
        fscanf(fp,"%c",&c);//继续下一个
    }
    fclose(fp);
    fclose(fw);
    return ;
}

(五)打印编码

void printf_code()
{
    FILE *fp,*fw;
    char temp;
    if((fp=fopen("codefile.txt","r"))==NULL)
        cout<<"error"<<endl;
    if((fw=fopen("codeprin.txt","w"))==NULL)
        cout<<"error"<<endl;
    cout<<"codefile.txt:"<<endl;
    fscanf(fp,"%c",&temp);
    for(int i=1;!feof(fp);i++)
    {
        printf("%c",temp);
        if(i%50==0)//50个一换行
            cout<<endl;
        fputc(temp,fw);//再写到codeprint文件中
        fscanf(fp,"%c",&temp);
    }
    cout<<endl;
    cout<<"编码已存入文件codeprin.txt"<<endl;
    fclose(fp);
    fclose(fw);
}

(六)打印哈夫曼树
(1)生成空格

void co_tree(unsigned char T[100][100],int s,int *m,int j,HuffmanTree HT)
{
    int k,l;
    l=++(*m);
    for(k=0;k<s;k++)//深度越深,前面空格越多
        T[l][k]=' ';
    T[l][k]=HT[j].weight;
    if(HT[j].lchild)
        co_tree(T,s+1,m,HT[j].lchild,HT);
    if(HT[j].rchild)
        co_tree(T,s+1,m,HT[j].rchild,HT);
    T[l][++k]='\0';
    return ;
}

(2)打印哈夫曼树

void printf_tree(int num,HuffmanTree HT)
{
    unsigned char T[100][100];
    FILE *fp;
    int m=0;
    co_tree(T,0,&m,2*num-1,HT);
    if((fp=fopen("treeprin.txt","w"))==NULL)
        cout<<"Open file error!"<<endl;
    for(int i=1;i<=2*num-1;i++)
    {
        for(int j=0;T[i][j]!='\0';j++)
        {
            if(T[i][j]==' ')//空格
               printf(" "),fputc(T[i][j],fp);
            else//字符
               printf("%u",T[i][j]),fprintf(fp,"%u",T[i][j]);
        }
         cout<<endl;
         fputc(10,fp);
    }
    fclose(fp);
    return ;
}

主函数部分:

int main()
{
    char a[100]; //存储要编码的所有字符
    char b[400];  //存储要翻译的二进制编码
    char c[100]; //存储翻译出来的结果
    HuffmanTree HT=NULL;
    char ** HC;
    while(1)
    {
        char s1[]= {"结点"},s2[]= {"字符"},s3[]= {"权值"},s4[]= {"双亲"},s5[]= {"左孩子"},s6[]= {"右孩子"};
        int flag=1,choose,num,cc=0;
        menu();
        char temp;
        cout<<"选择操作:";
        cin>>choose;
        switch(choose)
        {
        case 1:
            cout<<"输入字符个数:";
            cin>>num;
            cout<<"依次输入"<<num<<"个字符:";
            for(int i=1; i<=num; i++)
                cin>>a[i];
            cout<<"依次输入"<<num<<"个字符的权值:";
            CreatHuffmanTree(HT,num);
            //cout<<"结点i"<<"\t字符"<<"\t权值"<<"\t双亲"<<"\t左孩子"<<"\t右孩子"<<endl;
            //for(int i=1; i<=num*2-1; i++)
                //cout<<i<<"\t"<<a[i]<<"\t"<<HT[i].weight<<"\t"<<HT[i].parent<<"\t"<<HT[i].lchild<<"\t"<<HT[i].rchild<<endl;
            FILE *fp;
            if((fp=fopen("hfmtree.txt","w"))==NULL)
                cout<<"error"<<endl;
            fwrite(&s1,sizeof(s1),1,fp);fwrite(&s2,sizeof(s2),1,fp);fwrite(&s3,sizeof(s3),1,fp);
            fwrite(&s4,sizeof(s4),1,fp);fwrite(&s5,sizeof(s5),1,fp);fwrite(&s6,sizeof(s6),1,fp);
            fputc(10,fp);//10=/n
            for(int i=1; i<=2*num-1; i++)
            {
                fprintf(fp,"%-3d  ",i);
                fwrite(&a[i],1,1,fp);
                /*fprintf(fp,"    %-3d    ",HT[i].weight);
                fprintf(fp,"%-3d    ",HT[i].parent);
                fprintf(fp,"%-3d    ",HT[i].lchild);
                fprintf(fp,"%-3d    ",HT[i].rchild);
                fputc(10,fp);*/
            }
            fclose(fp);
            break;
        case 2:
            CreatHuffmanCode(HT,HC,num);
            cout<<"结点i\t"<<"字符\t"<<"权值\t"<<"编码\t"<<endl;
            for(int i=1; i<=num; i++)
                cout<<i<<"\t"<<a[i]<<"\t"<<HT[i].weight<<"\t"<<HC[i]<<endl;
            break;
        case 3:
            Coding(HT,HC,num,a);
            cout<<"二进制编码已存入codefile.txt"<<endl;
            break;
        case 4:
            cout<<"从codefile.txt文件中读取进行译码:"<<endl;
            if((fp=fopen("codefile.txt","rb"))==NULL)
                cout<<"error"<<endl;
            while(1)
            {
                temp = fgetc(fp); //读一个字节。
                if(temp == EOF) break; //到文件尾,退出循环。
                b[cc++] =temp ;//赋值到字符数组中。
            }
            b[cc]='\0';
            printf("%s\n",b);
            fclose(fp);
            TransCode(HT,b,a,c,num);
            cout<<"译码结果:";
            printf("%s\n",c);
            cout<<"翻译结果已存入textfile.txt"<<endl;
            break;
        case 5:
            printf_code();
            break;
        case 6:
            cout<<"打印哈夫曼树:"<<endl;
            printf_tree(num,HT);
            break;
        case 7:
            flag=0;
            break;
        default:
            cout<<"输入有误,重新输入"<<endl;
            continue;
        }
        if(flag==0)
            break;
    }
    return 0;
}
  • 6
    点赞
  • 147
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值