数据结构实训——哈夫曼编码/译码器

哈夫曼(Huffman)编/译码器(限1人完成)
【问题描述】
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站写一个哈夫曼码的编/译码系统。首先输入一段包含27个字符(包含空格)的文字(可以存在一个文件中),然后统计出各个字符出现的次数,以每个字符出现的次数为权值构造哈夫曼树,求得哈夫曼编码。
【基本要求】
一个完整的系统应具有以下功能:
1、 O: 输入一段字符(要包含27各字符)存入文件chartexfile中,统计出各个字符出现的次数并以表格的形式存入文件charsumfile中.
例如如下表:
字符 空格 A B C D E F G H I J K L M
频度 186 64 13 22 32 103 21 15 47 57 1 5 32 20
   
字符 N O P Q R S T U V W X Y Z  
频度 57 63 15 1 48 51 80 23 8 18 1 16 1

2、I:初始化(Initialization)。从终端读入字符集大小n,n个字符及n个权值,建立哈夫曼树,并将它存于文件hfmTree中。
3、 E:编码(Encoding)。利用以建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),
对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。
4、D:译码(Decoding)。利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。
5. P:打印代码文件(Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码。
同时将此字符形式的编码文件写入文件CodePrin中。
4、 T:打印哈夫曼树(Tree Printing)。将已在内存中的哈夫曼树以直观的方式(树或凹入
表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint中。
【测试数据】
用文件charsumfile给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:“THIS PROGRAM IS MY FAVORITE”。
测试数据要求:
要求使用1、全部合法数据;2、整体非法数据;3、局部非法数据。进行程序测试,以保证程序的稳定

#include
#include

#define maxval 10000.0
#define maxsize 10000   //哈夫曼编码的最大位数
typedef struct
{
    char ch;
    float weight=-1;
    int lchild,rchild,parent;
} hufmtree;
typedef struct
{
    char bits[10000];   //位串
    int start;      //编码在位串中的起始位置
    char ch;        //字符
} codetype;
int n,m;
hufmtree tree[20000];
codetype code[10000];
void TongJi(); //统计字符个数
void huffman(hufmtree tree[]);//建立哈夫曼树
void huffmancode(codetype code[],hufmtree tree[]);//根据哈夫曼树求出哈夫曼编码
void decode(hufmtree tree[]);//依次读入电文,根据哈夫曼树译码
void print(); //打印代码文件,输出 CodeFile.txt
void TreePrinting();//打印哈夫曼树
int main()
{
    while(1)
    {
        int t;
        printf("---------------菜单-------------------\n");
        printf("1.输入一段字符,统计出各个字符出现的次数.\n");
        printf("2.建立哈夫曼树.\n");
        printf("3.编码.\n");
        printf("4.译码.\n");
        printf("5.打印编码\n");
        printf("6.打印哈夫曼树\n");
        printf("7.退出\n");
        printf("---------------------------------------\n");
        printf("请选择你要进行的操作:");
        scanf("%d",&t);
        switch(t)
        {
        case 1:
            TongJi();
            break;
        case 2:
            huffman(tree);//建立哈夫曼树
            printf("哈夫曼树已建立!\n");
            break;
        case 3:
            huffmancode(code,tree);//根据哈夫曼树求出哈夫曼编码
            break;
        case 4:
            decode(tree);//依次读入电文,根据哈夫曼树译码*/
            break;
        case 5:
            print();
            break;
        case 6:
            TreePrinting();
            break;
        case 7:
            return 0;
        }
    }
    return 0;
}
void TongJi()  //统计字符个数
{
    FILE *out1,*out2;
    out1=fopen("chartexfile.txt","w");
    out2=fopen("charsumfile.txt","w");
    if(out1==NULL||out2==NULL)
    {
        printf("打开文件失败\n");
    }
    char s[1000];
    int i=0;
    char zf[1000]= {0}, z[1000];
    int cnt=1,k,num[1000]= {0};
    printf("请输入字符串(空格以-代替,按回车结束):");
    scanf("%s",s);
    k=strlen(s);
    fprintf(out1,"%s",s);
    for(i=0; i<k; i++)
    {
        if(zf[s[i]]==0)
        {
            zf[s[i]]=cnt++;
            z[cnt-1]=s[i];
        }
        num[zf[s[i]]]++;
    }
    printf("字符    频率\n");
    fprintf(out2,"字符    频率\n");
    for(int i=1; i<cnt; i++)
    {
        if(z[i]!='-')
            printf("%3c     %4d\n",z[i],num[zf[z[i]]]);
        else printf(" 空格   %4d\n",num[zf[z[i]]]);
        fprintf(out2,"%3c     %4d\n",z[i],num[zf[z[i]]]);
    }
    fclose(out1);
    fclose(out2);
}
void huffman(hufmtree tree[]) //建立哈夫曼树
{
    int i,j,p1,p2;//p1,p2分别记住每次合并时权值最小和次小的两个根结点的下标
    float small1,small2,f;
    char c;
    FILE *fp,*in;
    in=fopen("charsumfile.txt","r");
    fp=fopen("hfmTree.txt","w");
    if(fp==NULL)
    {
        printf("打开文件失败\n");
        return ;
    }
    for(i=0; i<20000; i++)  //初始化
    {
        tree[i].parent=0;
        tree[i].lchild=-1;
        tree[i].rchild=-1;
        tree[i].weight=0.0;
    }
    int t;
    printf("(1)根据字符频率文件 charsumfile.txt 生成哈夫曼树\n(2)手动输入字符频率生成哈夫曼树\n 请选择你要进行的操作:");
    scanf("%d",&t);
    if(t==2)
    {
        printf("请输入字符个数:");
        scanf("%d",&n);
        getchar();
        m=2*n-1;
        printf("请依次输入前 %d 个字符(中间用空格隔开,空格用‘-’代替):",n);
        for(i=0; i<n; i++)
        {
            tree[i].ch=getchar();
            getchar();
        }
        printf("请依次输入前 %d 个权值(中间用空格隔开)\n",n);
        for(i=0; i<n; i++) //读入前n个结点的字符及权值
        {
            scanf("%f",&f);
            tree[i].weight=f;
        }
    }
    else if(t==1)
    {
        i=0;
        char na[10];
        fscanf(in,"%s %s",na,na);
        while(EOF!=fscanf(in," %c %lf",&tree[i].ch,&tree[i].weight))
        {
            i++;
        }
        n=i;
        m=n*2-1;
    }
    for(i=n; i<m; i++)    //进行n-1次合并,产生n-1个新结点
    {
        p1=0;
        p2=0;
        small1=maxval;
        small2=maxval;   //maxval是float类型的最大值
        for(j=0; j<i; j++)  //选出两个权值最小的根结点
            if(tree[j].parent==0)
                if(tree[j].weight<small1)
                {
                    small2=small1;  //改变最小权、次小权及对应的位置
                    small1=tree[j].weight;
                    p2=p1;
                    p1=j;
                }
                else if(tree[j].weight<small2)
                {
                    small2=tree[j].weight;  //改变次小权及位置
                    p2=j;
                }
        tree[p1].parent=i;
        tree[p2].parent=i;
        tree[i].lchild=p1;  //最小权根结点是新结点的左孩子
        tree[i].rchild=p2;  //次小权根结点是新结点的右孩子
        tree[i].weight=tree[p1].weight+tree[p2].weight;
    }
    for(i=0; i<m; i++)
    {
        fprintf(fp,"%3d %3c %3.1f %3d %3d %3d\n",i+1,tree[i].ch,tree[i].weight,tree[i].parent,tree[i].lchild,tree[i].rchild);
    }
    fclose(fp);
    fclose(in);
}//huffman

void huffmancode(codetype code[],hufmtree tree[]) //根据哈夫曼树求出哈夫曼编码
{
//codetype code[]为求出的哈夫曼编码
//hufmtree tree[]为已知的哈夫曼树
    if(tree[0].weight==-1)  //如不在内存,则从文件hfmTree中读入
    {
        printf("\n未建立哈夫曼树,已从 hfmTree.txt 中读取哈夫曼树\n");
        FILE *fp;
        fp=fopen("hfmTree.txt","r");
        if(fp==NULL)
        {
            printf("打开 hfmTree.txt 文件失败!\n");
            return ;
        }
        int i=0,T;
        while(EOF!=fscanf(fp,"%d %c %f %d %d %d\n",&T,&tree[i].ch,&tree[i].weight,&tree[i].parent,&tree[i].lchild,&tree[i].rchild))
        {
            i++;
        }
        m=i;
        n=(m+1)/2;
        printf("%d %d\n\n",n,m);
        fclose(fp);
    }
    int i,c,j,p;
    codetype cd;   //缓冲变量
    for(i=0; i<n; i++)
    {
        cd.start=n;
        cd.ch=tree[i].ch;
        c=i;       //从叶结点出发向上回溯
        p=tree[i].parent;   //tree[p]是tree[i]的双亲
        while(p!=0)
        {
            cd.start--;
            if(tree[p].lchild==c)
                cd.bits[cd.start]='0';   //tree[i]是左子树,生成代码'0'
            else
                cd.bits[cd.start]='1';   //tree[i]是右子树,生成代码'1'
            c=p;
            p=tree[p].parent;
        }
        code[i]=cd;    //第i+1个字符的编码存入code[i]
    }
    printf("每个字符的哈夫曼编码如下:\n");
    for(i=0; i<n; i++)
    {
        if(code[i].ch=='-')
            printf("空格: ");
        else
        printf("%4c: ",code[i].ch);
        for(j=code[i].start; j<n; j++)
            printf("%c",code[i].bits[j]);
        printf("\n");
    }

    FILE *in,*out;
    in=fopen("ToBeTran.txt","r");
    out=fopen("CodeFile.txt","w");
    if(in==NULL||out==NULL)
    {
        printf("打开文件失败\n");
        return ;
    }
    printf("请选择你的操作 从文件 ToBeTran.txt 中读取(1)/手动输入字符(2):");
    int T,cnt=0;
    char s[10000];
    scanf("%d",&T);
    getchar();
    if(T==1)
    {
        while(EOF!=fscanf(in,"%c",&s[cnt]))
        {
            if(s[cnt]==' ')
                s[cnt]='-';
            cnt++;
        }
        s[cnt]=0;
        printf("字符串为:%s\n",s);
    }
    else if(T==2)
    {
        printf("请输入你要输入的字符串(空格用'-'代替):");
        scanf("%s",s);
    }
    printf("编码为:");
    for(int k=0; s[k]; k++)
    {
        for(int i=0; i<n; i++)
        {
            if(s[k]==code[i].ch)
            {
                for(int j=code[i].start; j<n; j++)
                {
                    fprintf(out,"%c",code[i].bits[j]);
                    printf("%c",code[i].bits[j]);
                }
            }
        }
    }
    printf("\n");
    fclose(in);
    fclose(out);
}//huffmancode

void decode(hufmtree tree[]) //依次读入电文,根据哈夫曼树译码
{
    int i=0,j,t;
    char b[maxsize];
    char endflag='2';    //电文结束标志取2
    FILE *in,*out;
    in=fopen("CodeFile.txt","r");
    out=fopen("TextFile.txt","w");
    if(in==NULL)
    {
        printf("打开 CodeFile.txt 文件失败!\n");
    }
    printf("请选择你的操作,从CodeFile.txt读入电文(1)/手动输入电文(2):");
    scanf("%d",&t);
    getchar();
    if(t==1)
    {
        printf("编码为:");
        i=0;
        while(EOF!=fscanf(in,"%c",&b[i]))
        {
            printf("%c",b[i]);
            i++;
        }
        b[i]='2';
        printf("%c\n\n",b[i]);
    }
    else if(t==2)
    {
        printf("请输入电文以 ‘2’ 结束:");
        scanf("%s",b);
    }
    else
    {
        printf("输入错误!请重新输入\n");
        decode(tree);
        return ;
    }
    i=m-1;             //从根结点开始往下搜索
    j=0;
    printf("译码后的字符为");
    while(b[j]!='2')
    {
        if(b[j]=='0')
            i=tree[i].lchild;   //走向左孩子
        else
            i=tree[i].rchild;   //走向右孩子
        if(tree[i].lchild==-1)     //tree[i]是叶结点
        {
            printf("%c",tree[i].ch);
            fprintf(out,"%c",tree[i].ch);
            i=m-1;      //回到根结点
        }
        j++;
    }
    printf("\n");
    if(tree[i].lchild!=-1&&b[j]!='2')   //电文读完,但尚未到叶子结点
        printf("\nERROR\n");  //输入电文有错
    fclose(in);
    fclose(out);
}//decode
void print()  //输出 Codefile.txt
{
    FILE *in,*out;
    char ch;
    int cnt=0;
    in=fopen("CodeFile.txt","r");
    out=fopen("CodePrin.txt","w");
    if(in==NULL||out==NULL)
    {
        printf("打开文件失败\n");
        return ;
    }
    while(EOF!=fscanf(in,"%c",&ch))
    {
        if(++cnt%50==0)
        {
            printf("\n");
            fprintf(out,"\n");
        }
        printf("%c",ch);
        fprintf(out,"%c",ch);
    }
    printf("\n");
    fprintf(out,"\n");
    fclose(in);
    fclose(out);
}
void TreePrinting()  //打印哈夫曼树
{
    FILE *in,*out;
    int num;
    in=fopen("hfmTree.txt","r");
    out=fopen("TreePrint.txt","w");
    if(in==NULL||out==NULL)
    {
        printf("打开文件失败\n");
        return ;
    }
    hufmtree a;
    printf("|--------------------------------------------|\n");
    fprintf(out,"|--------------------------------------------|\n");
    printf("|节点 字符 | 权值 | 父节点 | 左孩子 | 右孩子 |\n");
    fprintf(out,"|节点 字符 | 权值 | 父节点 | 左孩子 | 右孩子 |\n");
    while(EOF!=fscanf(in,"%d %c %f %d %d %d\n",&num,&a.ch,&a.weight,&a.parent,&a.lchild,&a.rchild))
    {
        printf("|%4d %3c %7.1f %7d %8d %7d   |\n",num,a.ch,a.weight,a.parent,a.lchild,a.rchild);
        fprintf(out,"|%4d %3c %7.1f %7d %8d %7d   |\n",num,a.ch,a.weight,a.parent,a.lchild,a.rchild);
    }
    printf("|--------------------------------------------|\n");
    fprintf(out,"|--------------------------------------------|\n");
    fclose(in);
    fclose(out);
}

你可能感兴趣的:(数据结构实训——哈夫曼编码/译码器)