哈夫曼编/译码器

来源:互联网 发布:淘宝连衣裙女装 编辑:程序博客网 时间:2024/06/04 08:38

【问题描述】

利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站写一个哈夫曼码的编/译码系统。

【基本要求】

一个完整的系统应具有以下功能:

(1)1:初始化(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中。

【测试数据】

(1)利用教科书例6-2中的数据调试程序。    ’

(2)用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:“THIS PROGRAM IS MY FAVORITE”。

【实现提示】

 (1)编码结果以文本方式存储在文件CodeFile中。

 (2)用户界面可以设计为“菜单”方式:显示上述功能符号,再加上“Q”,表示退出运行Quit。请用户键入一个选择功能符。此功能执行完毕后再显示此菜单,直至某次用户选择了“Q”为止。    ,

(3)在程序的一次执行过程中,第一次执行IDC命令之后,哈夫曼树已经在内存了,不必再读入。每次执行中不一定执行I命令,因为文件himTree可能早已建好。

1):初始化(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中。

输入的形式为:用户按照屏幕提示,选择相应的操作。在数据录入时,先输入字符,再输入空格或回车,再输入权值。

输出形式为:在屏幕上打印编码或原来的字符。 //输出结果可以是文件

程序执行的命令包括:

(1)数据录入 (2)数据输出

测试数据: 

1. 概要设计

2.    抽象数据类型

ADT BinaryTree{

数据对象:DD具有相同特性的数据元素的集合 

基本操作:

InitBiTree(&T)

    操作结果:构造二叉树T

DestroyBiTree(&T)

    初始条件:二叉树存在。

    操作结果:销毁二叉树T

CreatBitree(&T,definition);

初始条件:definition给出二叉树的定义

操作结果:按definition构造二叉树T

ValueTe

初始条件:二叉树存在,eT中某个节点

操作结果:返回e的值

ClearBiTree&T

初始条件:二叉树T存在

操作结果:若T为空二叉树,则返回TRUE,否则FALSE.

Root&T

初始条件:二叉树存在;

操作结果:返回T的根。

LeftChildT,e

初始条件:二叉树存在,eT中的一个节点。

操作结果:返回e的左孩子,若e无左孩子,则返回空。

RightChildT,e

初始条件:二叉树存在,eT中的一个节点。

操作结果:返回e的右孩子,若e无右孩子,则返回空。

Leftsibling(T,e)

初始条件:二叉树T存在,eT中的某个节点

操作结果:返回e的左兄弟,若eT的左孩子或无左兄弟,这返回空。

Ringtsibling(T,e)

初始条件:二叉树T存在,eT中的某个节点

操作结果:返回e的右兄弟,若eT的右孩子或无右兄弟,这返回空。

}ADT BinaryTree

2. 主要程序模块:本程序包含三个模块

1)主程序模块:

void main()

{

初始化;

do

{

接受命令;

处理命令;

}while (命令=“退出”)

}

2)节点结构的单元模块——定义树的节点结构。

3)二叉树的单元模块——实现哈夫曼树的抽象数据类型;

各模块的之间的调用关系如下:

                           主程序模块

                   

           节点的单元模块

                                    

                  

             二叉树的单元模块

3. 详细设计

  1.节点的详细定义

typedef struct{

char ch;

int weight;

int parent,lchild,rchild;

}HTNode,*HuffmanTree;

2.找出权最小的节点

void select(HuffmanTree HT,int n,int &s1,int &s2){          //找出权值最小的两个编号 

int i,min1,min2;

min1=min2=MAXVALUE;

for(i=1;i<=n;++i){

if(min1>HT[i].weight&&HT[i].parent==0){

min2=min1;

s2=s1;

min1=HT[i].weight;

s1=i;

}

else

if(min2>HT[i].weight&&HT[i].parent==0){

min2=HT[i].weight;

s2=i;

}

}

}//select

3. 哈夫曼树的详细定义:

void CreateHuffman(HuffmanTree &HT,int &n){        //构造赫夫曼树HT

int m,i,s1,s2;

do{

cout<<"请输入字符个数:";

cin>>n;

if(n<=1)

cout<<"输入错误!请重新输入!"<<endl;

}while(n<=1);

m=2*n-1;

HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));

cout<<"请输入字符和其对应的权值(字符 权值)"<<endl;

for(i=1;i<=n;++i){

cin>>HT[i].ch>>HT[i].weight;

HT[i].parent=0;

HT[i].lchild=HT[i].rchild=0;

}

for(i=n+1;i<=m;++i){

HT[i].weight=0;

HT[i].parent=0;

HT[i].lchild=HT[i].rchild=0;

}

for(i=n+1; i<=m; i++){ //构造赫夫曼树

select(HT,i-1,s1,s2);//HT[1..i-1]中选择parent0weight最小的两个结点,其序号为s1s2

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;

}

}//CreateHuffman

4.编码的详细定义

void EnCoding(HuffmanTree HT,HuffmanCode HC,int n){        //将字符串编码 

     int i,j;

     char PCode[MAXCODE];

 cout<<"请输入字符串:";

do{     

    cin>>PCode;

        cout<<PCode;

cout<<"编码为:";

for(i=0;PCode[i]!='/0';++i){

for(j=1;j<=n;++j){

if(HT[j].ch==PCode[i]){

cout<<HC[j];

break;

}

}

if(j==n+1){

cout<<endl<<"没找到字符"<<PCode[i]<<endl;

cout<<"请重新输入需要编码的字符串:";

break;

}

}

cout<<endl;

}while(j==n+1&&PCode[i]!='/0');        

}//EnCoding

5.译码的详细定义

void DeCoding(HuffmanTree HT,int n){                     //译码 

     char DCode[MAXCODE];

     int i,j;

     cout<<"请输入二进制编码:";

     cin>>DCode;

 cout<<DCode<<"译码为";

     for(i=0,j=2*n-1;DCode[i]!='/0';++i){

if(DCode[i]=='0')

j=HT[j].lchild;

else

j=HT[j].rchild;

if(!HT[j].lchild&& !HT[j].rchild){ //进入叶子结点

cout<<HT[j].ch;

j=2*n-1; //重新从树根出发进行译码

}

 }

     cout<<endl;    

}//DeCoding

5.销毁树的详细定义

void Destory(HuffmanTree &HT,HuffmanCode &HC,int n){                       //销毁指针,释放存储空间 

     int i;

     for(i=0;i<n;i++)

         free(HC[i]);

     free(HC);

     free(HT);        

}//Destroy

6.密码表输出的详细定义

void PrintCode(HuffmanTree HT,HuffmanCode HC,int n){       //输出每个字符所对应的编码 

int i;

for(i=1;i<=n;++i)

cout<<HT[i].ch<<" "<<HC[i]<<endl;

}//PrintCode

7.主函数和其它函数的伪码算法

int main()

{

    //主函数

    initialization();//初始化

do

    {

readcommand(cmd);//读入命令

interpret(cmd);//执行命令

}while (cmd!=q && cmd!=Q)

}

void initialization()

{

……………….

}

调试分析:

测试结果

测试输入:

输出:

 

 程序代码:

 #include<stdio.h>

#include<stdlib.h>

#include<string.h>

typedef struct

{

char ch;

    int weight;

    int selected;

    int parent,lchild,rchild;

}HTNode,*HuffmanTree;

 

typedef struct S

{

char c;

    struct S * next;

}S,*Sentence;

 

typedef char * * HuffmanCode;

 

typedef struct TreeNode

{

struct TreeNode * lchild;

    struct TreeNode * rchild;

    char code;

    char data;

}TreeNode,*Tree;

 

void Select(HuffmanTree &HT,int position,int *s1,int *s2)

{

int i,j,t;

    int p,q;

    int first,second;

    for(i=1;i<=position;i++)

if(HT[i].selected==0)

{

first=HT[i].weight;

            break;

}

    for(j=i+1;j<=position;j++)

        if(HT[j].selected==0)

{

            second=HT[j].weight;

            break;

}

    if(first>second)

{

        t=first;

        first=second;

        second=t;

 

    for(i=j+1;i<=position;i++)

{

if(HT[i].selected==0)

{

t=HT[i].weight;

            if(t<first)

{

                second=first;

                first=t;

}

            else if(t<second)

{

second=t;

}

}

}

for(i=1;i<=position;i++)

if(HT[i].selected==0&&HT[i].weight==first)

{

HT[i].selected=1;

            p=i;

            break;

}

}

    for(i=1;i<=position;i++)

{

if(HT[i].selected==0&&HT[i].weight==second)

{

HT[i].selected=1;

            q=i;

            break;

}

}

    if(p>q)

{

t=q;

        q=p;

        p=t;

}

    *s1=p;

    *s2=q;

    return;

}

 

 

void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int * &w,int n)

{

int m,i,j,s1,s2,start,f;

    char * cd;

    HuffmanTree p;

m=2*n-1;

    if(n<=1)

return;

    HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));

    for(p=HT+1,i=1;i<=n;i++,p++,w++)

{

p->weight=*(w+1);

        p->selected=p->lchild=p->rchild=p->parent=0;

}

    for(;i<=m;i++,p++)

p->selected=p->parent=p->lchild=p->rchild=0;

 

    for(i=n+1;i<=m;i++)

{

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;

}

 

    printf("/n哈弗曼树表:/n");

    printf("    编号   权重   双亲  左孩子 右孩子/n");

    for(p=HT+1,i=1;i<=m;i++,p++)

    printf("%7d%7d%7d%7d%7d/n",i,p->weight,p->parent,p->lchild,p->rchild);

    printf("/n");

 

    HC=(HuffmanCode)malloc((n+1)*sizeof(char *));

    cd=(char *)malloc(n*sizeof(char));

    cd[n-1]='/0';

    for(i=1;i<=n;i++)

{

start=n-1;

 

    for(j=i,f=HT[i].parent;f!=0;j=f,f=HT[f].parent)

{

if(HT[f].lchild==j)

cd[--start]='0';

        else

            cd[--start]='1';

    }  

    HC[i]=(char *)malloc((n-start)*sizeof(char));

    strcpy(HC[i],&cd[start]);

}

    free(cd);

 

    printf("/n哈弗曼编码HC:/n");

    for(i=1;i<=n;i++)

{

printf("%d: %s/n",i,HC[i]);

}

    printf("/n");

}

 

void CodeInput(int n,int* &w,char* &code)

{

int i;

    w=(int *)malloc((n+1)*sizeof(int));

    code=(char *)malloc((n+1)*sizeof(char));

    printf("请输入字符以及权值:/n");

    for(i=1;i<=n;i++)

{

fflush(stdin);

scanf("%c %d",&code[i],&w[i]);

}

}

 

 

void Encoding(Sentence &sentence,char* &code,HuffmanCode &HC,int n)

{

char c,t,*pp;

    int i;

    Sentence p,q;

    sentence=(Sentence)malloc(sizeof(S));

    q=sentence;

    q->next=NULL;

    printf("请输入待编码的字符串:/n");

    fflush(stdin);

 

    while(scanf("%c",&c)!=EOF&&c!=10)

{

for(i=1;i<=n;i++)

{

t=code[i];

            if(c==t)

{

pp=HC[i];

                break;

}

}

        for(i=0;pp[i]!='/0';i++)

{

p=(Sentence)malloc(sizeof(S));

            p->c=pp[i];

            q->next=p;

            p->next=NULL;

            q=p;

}

}

    p=sentence->next;

 

    printf("/n编码后的语句:/n");

    while(p!=NULL)

{

printf("%c",p->c);

        p=p->next;

}

    printf("/n");

}

 

 

void Decoding(Sentence &sentence,char* &code,HuffmanCode &HC,Tree &root,int n)

{

Tree p,q;

    Sentence r;

    int i,j;

    char c,*t;

r=sentence->next;

    root=(Tree)malloc(sizeof(TreeNode));

    root->lchild=NULL;

    root->rchild=NULL;

    for(i=1;i<=n;i++)

{

        t=HC[i];

        q=root;

        for(j=0;j<strlen(HC[i]);j++)

{

            c=t[j];

            if(q->lchild!=NULL)

{

if(q->lchild->code==c)

{

q=q->lchild;    

}

                else if(q->rchild!=NULL)

{

                    q=q->rchild;     

}

                else

{

                    p=(Tree)malloc(sizeof(TreeNode));

                    p->lchild=NULL;

                    p->rchild=NULL;

                    p->code=c;

                    if(t[j+1]!='/0')

                        p->data='n';

                    else

                        p->data=code[i];

                        q->rchild=p;

                        q=p;

}

}

else{

p=(Tree)malloc(sizeof(TreeNode));

                p->lchild=NULL;

                p->rchild=NULL;

                p->code=c;

                if(t[j+1]!='/0')

                    p->data='n';

                else

                    p->data=code[i];

                q->lchild=p;

                q=p;

}

}

}

    q=root;

    printf("/n解码后的语句:/n");

    while(r!=NULL)

{

        c=r->c; 

        if(q->lchild!=NULL)

{

            if(q->lchild->code==c)

{

                q=q->lchild;

                if(q->lchild==NULL)

{

                    printf("%c",q->data);

                    q=root;

}

}

            else

{

                q=q->rchild; 

                if(q->lchild==NULL)

                    printf("%c",q->data);

                    q=root;

}

}

}

        else

{

            printf("%c",q->data);

            q=root;

}

r=r->next;

}

    printf("/n/n");

    return;

}

 

void print(HuffmanTree &HT,int i,int space)

{

    int j;

    if(i)

{

        print(HT,HT[i].rchild,space+5);

        for(j=0;j<=space;j++)

            printf(" ");

        printf("%d/n",HT[i].weight);

        print(HT,HT[i].lchild,space+5);

}

    return ;

}

 

void main()

{

HuffmanTree HT;

    HuffmanCode HC;

    Tree root;

    int *w,n;

    char *code;

Sentence sentence;

    printf("请输入字符集大小n:/n");

    scanf("%d",&n);

    CodeInput(n,w,code);

    HuffmanCoding(HT,HC,w,n);

    Encoding(sentence,code,HC,n);

    Decoding(sentence,code,HC,root,n);

    printf("/n哈弗曼树/n");

    print(HT,2*n-1,0);

}