数据结构课程设计第三题。

来源:互联网 发布:网络电影女演员 编辑:程序博客网 时间:2024/04/26 14:17

一·课程设计题目:

     题目3.在一个加密应用中,要处理的信息来自下面的字符集,各个字符的相关使用频度如下:

字符空格 A  B  C  D  E F  G   H  I   J  K L M
频度 180 64 13 23  32103 22 15  47 57  1 5  31  20
字符 N O  P   Q R  S T   U  V  W X Y  Z
频度 55 63  15 1 48  56 80 25 7  18  2  16  1
现请编写程序你实现如下功能:

(1)运行时,由用户输入来初始化字符集大小和相应用字符。

(2)输入一个要加密的字符串,将其加密。

(3)输出解密字符串。

  二·分析:

     1.本题目要使自定义的字符集最快地获得一组编码,同时要防止数据的冗余,可以确定要使用哈夫曼编码,题目可以看成为:从键盘任意输入字符集,统计字符的频度并建立哈夫曼树,输出哈夫曼编码。

       哈夫曼树的原理就是在给定的N个实数中(N≥2),带权路径长度最小的二叉树就是哈夫曼树,即所有的叶结点的权值乘上其到根结点的路径长度,用公式表达就是L=W1*L1+W2*L2+W3*L3+...+Wn*Ln。根据字符出现的概率来构造平均长度最短的编码。它是一种变长的编码。在编码中,若各码字长度严格按照码字所对应符号出现概率的大小的逆序排列,则编码的平均长度是最小的。(其长度因符号出现的概率而不同,所以说哈夫曼编码是变长的编码。)

       在构造哈夫曼编码时,将哈夫曼树树中左分支和右分支分别标记为01;将从根到叶子的路径上的标号依次相连,作为该叶子所表示字符的编码。该编码即为哈夫曼编码。

      2.加密和解密

       有了字符集的哈夫曼编码表之后,对数据文件的编码过程是:依次读人文件中的字符c,在哈夫曼编码表H中找到此字符,若H.ch=c,则将字符c转换为H.bits中存放的编码串。

 

对压缩后的数据文件进行解码则必须借助于哈夫曼树T,其过程是:依次读人文件的二进制码,从哈夫曼树的根结点(T[m-1])出发,若当前读人0,则走向左孩子,否则走向右孩子。一旦到达某一叶子T时便译出相应的字符H.ch。然后重新从根出发继续译码,直至文件结束。

     3.源程序解析

        用宏定义,定义了N27,表示27个字符;M2×N1,表示huffman树的结点个数。定义了关于huffman的几个结构,见程序注释。为了方便,定义了一些关于huffman树、huffman编码的外部变量。

 三·源程序:

 #include<stdio.h>

 

#defineN27      //空格+26个大写字母

 

#defineM2*N-1

 

#defineinfinity32767  //宏定义

 

 

structnode               //huffman树的结点结构

 

{

 

       intweight;           //结点权值

 

       intplink,llink,rlink;          //双亲,左孩子,右孩子

 

};

 

 

 

structcodetype          //huffman编码结构

 

{

 

       intstart;              //起始位置

 

       charbits[N+1];       //存放01的数组

 

};

 

 

 

structelement           //字符及其编码的结构

 

{

 

       charsymbol;            //字符

 

       structcodetype code;        //字符编码

 

};

 

 

 

structnodetree[M+1];           //n个结点的huffman

 

structelementtable[N+1],t[100];       //n个结点的huffman编码表,报文编码

 

int x1,x2;

 

 

 

void sethuftree();                  //有关函数声明

 

void select0(ints);

 

void sethufcode();

 

void setcode();

 

void printtree();

 

void printhufcode();

 

 

 

void main()                    //**********主函数**********

 

{

 

      sethuftree();

 

      printtree();

 

      sethufcode();

 

      printhufcode();

 

      setcode();

 

      printf("\n\n");

 

}

 

 

 

void sethuftree()      //建立huffuman

 

{

 

       inti,treeweight[N]={180,64,13,23,32,103,22,15,47,57,1,5,32,20,55,63,15,1,48,56,80,25,7,18,2,16,1};

 

       chartablesymbol[N]={' ','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};

 

      

 

       for(i=1;i<=M;i++)                  //初始双亲,左,右结点为:0

 

             tree[i].plink=tree[i].llink=tree[i].rlink=0;

 

       for(i=1;i<=N+1;i++)       //初始化27个字符及其权值

 

       {

 

             table[i].symbol=tablesymbol[i-1];

 

             tree[i].weight=treeweight[i-1];

 

       }

 

       for(i=N+1;i<=M;i++)              //找权值最小的2个结点,组成huffman

 

       {

 

             select0(i-1);           //调用找权值最小的2个结点函数

 

             tree[x1].plink=i;

 

             tree[x2].plink=i;

 

             tree[i].llink=x1;

 

             tree[i].rlink=x2;

 

              tree[i].weight=tree[x1].weight+tree[x2].weight;

 

       }

 

}

 

 

 

void select0(ints)             //找权值最小的2个结点

 

{

 

       inti;

 

       floatv1,v2;

 

       v1=v2=infinity;

 

      x1=x2=0;

 

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

 

             if(tree[i].plink==0)

 

                    if(tree[i].weight<v1)

 

                    {

 

                           v2=v1;

 

                           x2=x1;

 

                           v1=tree[i].weight;

 

                           x1=i;

 

                    }

 

                     elseif(tree[i].weight<v2)

 

                    {

 

                           v2=tree[i].weight;

 

                           x2=i;

 

       }

 

}

 

 

 

void printtree()           //输出huffman

 

{

 

       inti;

 

      printf("哈夫曼树为:\n\n");

 

      printf("结点值权值(频度)双亲  左孩子 右孩子\n");

 

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

 

             printf(%-8c%-11d%-8d%-8d%d\n",table[i].symbol,tree[i].weight,tree[i].plink,tree[i].llink,tree[i].rlink);

 

       for(i=N+1;i<=M;i++)

 

             printf("\t %-11d%-8d%-8d%d\n",tree[i].weight,tree[i].plink,tree[i].llink,tree[i].rlink);

 

      printf("\n\n------------------------------------------------------------------\n\n\n");

 

}

 

 

 

void sethufcode()          //建立huffman编码

 

{

 

       inti,s,f,k=1;

 

       structcodetype c;

 

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

 

       {

 

             c=table[i].code;

 

             c.start=N+1;

 

             s=i;

 

             f=tree[s].plink;

 

             do

 

             {

 

                     c.start--;

 

                    if(s==tree[f].llink)

 

                           c.bits[c.start]='0';

 

                    else

 

                           c.bits[c.start]='1';

 

                    s=f;

 

                    f=tree[s].plink;

 

             }while(f);

 

             table[i].code=c;

 

       }

 

}

 

 

 

void printhufcode()                  //输出huffman编码,即空格+26个大写字母

 

{

 

       inti,j;

 

       structcodetype c;

 

      printf("编码为:\n");

 

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

 

       {

 

             printf("%c:",table[i].symbol);

 

             c=table[i].code;

 

             for(j=c.start;j<=N;j++)

 

                     printf("%c",c.bits[j]);

 

             printf("\n");

 

       }

 

      printf("\n\n--------------------------------------------------------------------\n\n");

 

}

 

 

 

void setcode()                 //对报文进行编码

 

{

 

       inti,j,count;

 

       char c0,k='Y';

 

       structcodetype c;

 

       while(k=='Y')                  //利用循环,实现对报文多次编码

 

       {

 

      printf("\n\n请输入字符集(大写字母+空格,不超过100),输入'.'(+回车)号结束\n\n");

 

       for(i=1;i<=100;i++)          //利用循环,实现对报文输入

 

       {

 

             c0=getchar();

 

             t[i].symbol=c0;

 

             if(c0=='.')break;

 

       }

 

 

 

      count=i;                          //记录报文个数

 

      printf("\n\n--------------------------------------------------------------------\n\n");

 

      printf("\n\n输出的解密字符串为:\n\n");

 

       for(i=1;i<=count;i++)              //利用循环,实现对报文输出

 

       {

 

             if(t[i].symbol=='.')break;

 

             else

 

             switch(t[i].symbol)           //利用开关语句,查找编码

 

             {

 

       case' ':

 

              c=table[1].code;

 

             for(j=c.start;j<=N;j++)              //输出编码

 

                    printf("%C",c.bits[j]);

 

             printf(" ");

 

             break;

 

 

 

       case'A':

 

             c=table[2].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'B':

 

             c=table[3].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

              printf("");

 

             break;

 

       case'C':

 

             c=table[4].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

 

 

       case'D':

 

              c=table[5].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

 

 

       case'E':

 

             c=table[6].code;

 

             for(j=c.start;j<=N;j++)

 

                     printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'F':

 

             c=table[7].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

              break;

 

       case'G':

 

             c=table[8].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'H':

 

             c=table[9].code;

 

              for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'I':

 

             c=table[10].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'J':

 

             c=table[11].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'K':

 

             c=table[12].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'L':

 

             c=table[13].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'M':

 

             c=table[14].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

              printf("");

 

             break;

 

       case'N':

 

             c=table[15].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'O':

 

             c=table[16].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'P':

 

             c=table[17].code;

 

             for(j=c.start;j<=N;j++)

 

                     printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'Q':

 

             c=table[18].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

              break;

 

       case'R':

 

             c=table[19].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'S':

 

             c=table[20].code;

 

              for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'T':

 

             c=table[21].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'U':

 

             c=table[22].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'V':

 

             c=table[23].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'W':

 

             c=table[24].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'X':

 

             c=table[25].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'Y':

 

             c=table[26].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       case'Z':

 

             c=table[27].code;

 

             for(j=c.start;j<=N;j++)

 

                    printf("%c",c.bits[j]);

 

             printf(" ");

 

             break;

 

       default:

 

             printf(*此编码找不到,请检查输入是否正确");

 

             }

 

       }

 

       printf("\n\n\n--------------------------------------------------------------------\n\n");

 

      printf("\n\n\n-----  是否继续?(Y/N) -----\n");

 

      getchar();

 

      printf("\n");

 

       scanf("%c",&k);

 

      getchar();

 

      printf("\n\n*********************************************************\n\n");

 

       }

 

}

·总结

       程序运行有三个部分:第1部分,为27个字符的huffman树;第2个部分,为huffman编码,第三部分为解密字符串。

·体会

     在熟练掌握二叉树的基础上理解哈夫曼树和哈夫曼编码,熟知二叉树的性质及哈夫曼树为什么没有度为1的节点,理解节电总数M与叶子节电之间的关系为2*N-1

        程序是在不断的摸索尝试中才能越来越熟练的,光靠书本上背语法之类的不能做出程序,这个程序也是,我在参考了网上的方法之后才知道用哈夫曼树加密解密的方法,书上有没有提我没有注意到,真正实践了才知道。

0 0
原创粉丝点击