数据结构课程设计报告,哈夫曼树

 

 

 

 

数据结构课程设计 

 

实验题目:赫夫曼编码/译码器 

 

 

       

 

 

 

 

 

 

 

 

 

 

一、需求分析 

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

 

 

二、基本要求 

    1.基本要求 

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

     (1) I:初始化(Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立赫夫曼树,并将它存于文件hfmTree中。 

 (2) E:编码(Encoding)。利用已建好的赫夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。 

 (3) D:译码(Decoding)。利用已建好的赫夫曼树将文件CodeFile中的代码进行译码,结果存入文件Textfile中。 

2.测试要求 

(1) 利用教科书例6-2的数据调试程序:已知某系统在通信联络中只可能出现八种字符,其频率分别0.05,0.29,0.07,0.08,0.14,0.23,0.03,0.11,试设计赫夫曼编码。 

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

 

字符 

 

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

 

 

3.实现提示

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

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

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

概要设计 

   程序主要分为五部分,哈夫曼节点的定义,哈夫曼编码的定义,Select函数选择出权值最小的节点,Initialization函数生成哈夫曼树,主函数。

  

四、详细设计

1. 数据存储结构设计

1哈夫曼节点

 哈夫曼节点数据类型如下,包含字符,权值,父节点,左右孩子:

typedef struct{

        char ch;

        unsigned int weight;

        unsigned int parent,lchild,rchild;       

}HTNode,*HfmTree;

(2)哈夫曼编码

     哈夫曼编码用二级指针存储,动态分配空间:

typedef char **HfmCode;

2. 算法的设计思想

   1Select函数

 

void Select(HfmTree HT,int a,int &s1,int &s2){
     //选择出parent为0的两个权值最小的两个节点,s1节点权值小于s2节点
     int i,temp;
     i=0;
     while(HT[++i].parent!=0);
     s1=i;
     while(HT[++i].parent!=0);
     s2=i++;
     if(HT[s1].weight>HT[s2].weight){
        temp=s1;
        s1=s2;
        s2=temp;
     }
     for(;i<=a;i++){
        if(HT[i].parent==0)
           if(HT[i].weight<HT[s1].weight)  s1=i;
             else if(HT[i].weight<HT[s2].weight) s2=i;
     }
}//选出权值最小且无父节点的节点,选择范围为i-a;

 

   2Initialization函数

 

void Initialization(HfmTree &HT,HfmCode &HC,int n,string ch,int *w)
   {
     int i,m,s1,s2,start,c,f;
//i HTNode位置,m为哈夫曼树节点数,s1s2选择节点,start开始点,c当前节点,f父节点
     char* cd;
     if(n<=1) return;
     m=2*n-1;
     HT=(HfmTree)malloc((m+1)*sizeof(HTNode));//0号单元使用
     for(i=1;i<=n;i++){
        HT[i].ch=ch[i-1];
        HT[i].weight=w[i-1];
        HT[i].parent=0;
        HT[i].lchild=0;
        HT[i].rchild=0;
     }//初始化叶子节点

     for(;i<=m;i++){
        HT[i].ch='0';
        HT[i].weight=0;
        HT[i].parent=0;
        HT[i].lchild=0;
        HT[i].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;
     }
     HC=(HfmCode)malloc((n+1)*sizeof(char*));
     cd=(char*)malloc(n*sizeof(char));
     cd[n-1]='';
     for(i=1;i<=n;i++){
        start=n-1;
        for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent){
           if(HT[f].lchild==c){
              cd[--start]='0';
           }else{
              cd[--start]='1';
           }
        HC[i]=(char*)malloc((n-start)*sizeof(char));
        strcpy(HC[i],&cd[start]);
        }
     }
     free(cd);
     cout<<"生成成功!"<<endl<<endl;;
     cout<<"IDtchart"<<"weight"<<"t"<<"parent"<<"t"<<"lchild"<<"t"<<"rchild"<<"t"<<endl;
     for(i=1;i<2*n;i++){
          cout<<i<<"t"<<HT[i].ch<<"t"<<HT[i].weight<<"t"<<HT[i].parent<<"t"<<HT[i].lchild<<"t"<<HT[i].rchild<<"t"<<endl;
     }
     cout<<"chart"<<"Codet"<<endl;
     for(i=1;i<=n;i++){
           cout<<HT[i].ch<<"t"<<HC[i]<<"t"<<endl;
    }
}
 //生成,前半部分为生成哈夫曼树,调用Select得出权值最小的两项,计算权值
 //后半部分为哈夫曼编码生成,从后到前生成,动态分配空间大小

 

 

3.源程序

 

#include<iostream>
#include<fstream>
#include<string>
using namespace std;
typedef struct{
        char ch;
        unsigned int weight;
        unsigned int parent,lchild,rchild;
}HTNode,*HfmTree;

typedef char **HfmCode;

void Select(HfmTree HT,int a,int &s1,int &s2){
     //选择出parent为0的两个权值最小的两个节点,s1节点权值小于s2节点
     int i,temp;
     i=0;
     while(HT[++i].parent!=0);
     s1=i;
     while(HT[++i].parent!=0);
     s2=i++;
     if(HT[s1].weight>HT[s2].weight){
        temp=s1;
        s1=s2;
        s2=temp;
     }
     for(;i<=a;i++){
        if(HT[i].parent==0)
           if(HT[i].weight<HT[s1].weight)  s1=i;
             else if(HT[i].weight<HT[s2].weight) s2=i;
     }
}


void Initialization(HfmTree &HT,HfmCode &HC,int n,string ch,int *w)
   {
     int i,m,s1,s2,start,c,f;
     //i HTNode位置,m为哈夫曼树节点数,s1s2选择节点,start开始点,c当前节点,f父节点
     char* cd;
     if(n<=1) return;
     m=2*n-1;
     HT=(HfmTree)malloc((m+1)*sizeof(HTNode));//0号单元使用
     for(i=1;i<=n;i++){
        HT[i].ch=ch[i-1];
        HT[i].weight=w[i-1];
        HT[i].parent=0;
        HT[i].lchild=0;
        HT[i].rchild=0;
     }//初始化叶子节点

     for(;i<=m;i++){
        HT[i].ch='0';
        HT[i].weight=0;
        HT[i].parent=0;
        HT[i].lchild=0;
        HT[i].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;
     }
     HC=(HfmCode)malloc((n+1)*sizeof(char*));
     cd=(char*)malloc(n*sizeof(char));
     cd[n-1]='';
     for(i=1;i<=n;i++){
        start=n-1;
        for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent){
           if(HT[f].lchild==c){
              cd[--start]='0';
           }else{
              cd[--start]='1';
           }
        HC[i]=(char*)malloc((n-start)*sizeof(char));
        strcpy(HC[i],&cd[start]);
        }
     }
     free(cd);
     cout<<"生成成功!"<<endl<<endl;;
  cout<<"IDtchart"<<"weight"<<"t"<<"parent"<<"t"<<"lchild"<<"t"<<"rchild"<<"t"<<endl;
     for(i=1;i<2*n;i++){
cout<<i<<"t"<<HT[i].ch<<"t"<<HT[i].weight<<"t"<<HT[i].parent<<"t"<<HT[i].lchild<<"t"<<HT[i].rchild<<"t"<<endl;
     }
     cout<<"chart"<<"Codet"<<endl;
     for(i=1;i<=n;i++){
           cout<<HT[i].ch<<"t"<<HC[i]<<"t"<<endl;
    }
}



int main(){
    ifstream input_file;
    ofstream output_file;
    char action;
    string toBeTranSource,textFileSource;
    string charsIn,code;
    int i,j,c,n,w[100];
    HfmTree HT;
    HfmCode HC;
    while(action!='Q'){
        cout<<endl<<"**********************************************"<<endl;
        cout<<"*          哈夫曼编码/译码器                 *"<<endl;
        cout<<"*                                            *"<<endl;
        cout<<"*  I.初始化  E.编码  D.译码 /P.印码  /T.印树 *"<<endl;
        cout<<"*  C.清屏                                    *"<<endl;
        cout<<"*     Code by whocaresu    16/12/2010        *"<<endl;
        cout<<"**********************************************"<<endl;
        cout<<endl<<"请输入操作码:";
        cin>>action;
        action=toupper(action);
        switch(action){
                       case 'I':
                            // 输入信息
                            cout<<"请输入字符数:";
                            cin>>n;
                            cout<<"请输入字符组(紧密输入不用空格区分):";
                            getline(cin,charsIn);
                            getline(cin,charsIn);
                            cout<<"输入各个权重(中间空格):";
                            for(i=1;i<=n;i++){
                            cin>>w[i-1];
                            }
                            Initialization(HT,HC,n,charsIn,w);
                            //输出备份到文件
                            cout<<"正在输出备份到hfmTree.txt..."<<endl;
                            output_file.open("hfmTree.txt");
                            if(!output_file){
                                 system("cls");
                                 cout<<"Can't open file!"<<endl;return 1;
                            }
                            output_file<<n;
                            output_file<<charsIn<<"n";
                            for(i=0;i<n;i++){
                               output_file<<w[i]<<" ";
                            }
                            output_file<<"--------以下为具体编码信息--------"<<endl;
                            output_file<<"ChartCodet"<<endl;;
                            for(i=1;i<=n;i++){
                            output_file<<HT[i].ch<<"t"<<HC[i]<<"t"<<endl;
                            }
                            output_file.close();
                            cout<<"备份到hfmTree.txt成功!"<<endl;
                            ;break;

                       case 'E':
                            //判断内存中是否有生成哈夫曼树,没有的话从文件读入
                            if(sizeof(HC)<5){
                           cout<<"内存中不存在哈夫曼树,从hfmTree.txt读入!"<<endl;
                               input_file.open("hfmTree.txt");
                               if(!input_file){
                                 system("cls");
                                 cout<<"Can't open file!"<<endl;return 1;
                               }
                               input_file>>n;
                               getline(input_file,charsIn);
                               for(i=1;i<=n;i++){
                                  input_file>>w[i-1];
                               }
                               input_file.close();
                               Initialization(HT,HC,n,charsIn,w);
                            }
                            input_file.open("ToBeTran.txt");
                            if(!input_file){
                                 system("cls");
                                 cout<<"Can't open file!"<<endl;return 1;
                            }
                            cout<<"-------从ToBetTran.txt读取数据为--------"<<endl;
                            getline(input_file,toBeTranSource);
                            cout<<toBeTranSource<<endl;
                            input_file.close();
                            cout<<"----------------------------------------"<<endl;
                            output_file.open("CodeFile.txt");
                            if(!output_file){
                                 system("cls");
                                 cout<<"Can't open file!"<<endl;return 1;
                            }
                            cout<<"------编译的密文(输出到CodeFile.txt)----"<<endl;
                            for(i=0;i<=toBeTranSource.length();i++){
                               for(j=1;j<=n;j++){
                                  if(toBeTranSource[i]==HT[j].ch){
                                     cout<<HC[j];
                                     output_file<<HC[j];
                                  }
                               }
                            }
                            output_file.close();
                            cout<<endl<<"------------编译完成--------------------"<<endl;
                            ;break;
                      case 'D':
                           //译码
                           input_file.open("CodeFile.txt");
                           if(!output_file){
                                 system("cls");
                                 cout<<"Can't open file!"<<endl;
                                 return 1;
                           }
                           cout<<"------------读取的编码为----------------------"<<endl;
                           getline(input_file,code);
                           cout<<code<<endl;
                           input_file.close();
                           cout<<"----------------译码为--------------------"<<endl;
                           c=2*n-1;
                           output_file.open("TextFile.txt");
                           for(i=0;i<=code.length();i++){
                               if(HT.lchild==0){
                                 cout<<HT.ch;
                                 c=2*n-1;
                               }
                               if(code[i]=='0') c=HT.lchild;
                               else c= HT.rchild;
                           }
                           cout<<endl;
                           cout<<"-------------------------------------------"<<endl;
                           ;break;
                      case 'C':
                           system("cls");
                           break;
                      default:
                              cout<<endl<<"操作码错误,重新输入!"<<endl;
        }
   }
   system("pause");
   return 0;
    }

 

 

 

五、调试分析

1.测试数据列表:

(1) 利用教科书例6-2的数据调试程序:0.05,0.29,0.07,0.08,0.14,0.23,0.03,0.11

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

 abcdefghijklmnopqrstuvwxyz

186 64 13 22 32 103 21 15 47 57 1 5 32 20 57 63 15 1 48 51 80 23 8 18 1 16 1


 

 

 

 

 

 

 

 

 

 

、课程设计总结及心得体会

     不得不说,这个是目前做到的最复杂的一个程序,与停车场相比,它的函数,数据存储结构的定义少了不少,但是其复杂度却比停车场有过之无不及,文件操作比较复杂,为此我查阅了相关资料,做到了基本能够实现文件的读取和写入,程序中依然存在一个问题,那就是它只能读取一段文字,遇到回车会终止读入,这也局限了程序的使用面。

     在开发哈夫曼编译器的时候,发现一个整体概念很重要,要很清晰地知道它具体是怎么运作的。双亲结点,左右孩子,字符,权值组成哈夫曼节点,二级指针来动态存储哈夫曼编码,然后分配空间,调用Select函数帮助生成哈夫曼树,最后才生成哈夫曼节点。

     这次课程设计,加深了我对哈夫曼树的理解,巩固了数据结构的知识。但是我认为其中更加重要的是实践的一个过程,实践中学习,清楚了理论与实践之间的差距。用google搜索相关的资料,然后从资料中获取自己需要的信息,化为己用,这种学习的能力的锻炼,我认为才是最为重要的。

     

 

Chrome 常用键盘快捷键

Chrome 常用键盘快捷键

如果你是初次使用Chrome,有可能你会感觉Chrome好像缺少了某些东西似的,没错,为了让用户获得最大的浏览界面,Chrome连工具栏都缩成一个小小的按钮了。别以为这样会很不方便,Chrome提供了大量的键盘快捷方式,让你轻松打开你想要的功能。

窗口和标签页快捷键

 

Ctrl+N 打开新窗口
Ctrl+T 打开新标签页
Ctrl+Shift+N 在隐身模式下打开新窗口
Ctrl+O,然后选择文件 在谷歌浏览器中打开计算机上的文件
按住 Ctrl 键,然后点击链接 从后台在新标签页中打开链接,但您仍停留在当前标签页中
按住 Ctrl+Shift 键,然后点击链接 在新标签页中打开链接,同时切换到新打开的标签页
按住 Shift 键,然后点击链接 在新窗口中打开链接
Alt+F4 关闭当前窗口
Ctrl+Shift+T 重新打开上次关闭的标签页。谷歌浏览器可记住最近关闭的 10 个标签页。
将链接拖动到标签页内 在指定标签页中打开链接
将链接拖动到两个标签页之间 在标签页横条的指定位置建立一个新标签页,在该标签页中打开链接
Ctrl+1 到 Ctrl+8 切换到指定位置编号的标签页。您按下的数字代表标签页横条上的相应标签位置。
Ctrl+9 切换到最后一个标签页
Ctrl+Tab 或Ctrl+PgDown 切换到下一个标签页
Ctrl+Shift+Tab Ctrl+PgUp 切换到上一个标签页
Ctrl+W 或 Ctrl+F4 关闭当前标签页或弹出式窗口
Alt+Home 打开主页

地址栏快捷键

在地址栏,进行下列操作之一:

 

键入搜索字词 使用默认搜索引擎进行搜索
键入网址中”www.”和”.com”之间的部分,然后按 Ctrl+Enter 为您在地址栏中输入的内容添加”www.”和”.com”,然后打开网址
键入搜索引擎关键字或网址,按 Tab 键,然后键入搜索字词 使用与关键字或网址相关联的搜索引擎进行搜索。如果谷歌浏览器可以识别您要使用的搜索引擎,则会提示您按 Tab 键。
F6 或 Ctrl+L 或 Alt+D 选中网址区域中的内容
键入网址,然后按Alt+Enter 键 在新标签页中打开网址

打开谷歌浏览器各功能的快捷键

 

Ctrl+B 打开和关闭书签栏
Ctrl+Shift+B 打开书签管理器
Ctrl+H 查看”历史记录”页
Ctrl+J 查看”下载”页
Shift+Escape 查看任务管理器
Shift+Alt+T 将焦点设置在工具栏上。使用键盘上的向右和向左箭头,可导航至工具栏上的不同按钮。

网页快捷键

 

Ctrl+P 打印当前页
Ctrl+S 保存当前页
F5 重新加载当前页
Esc 停止加载当前页
Ctrl+F 打开”在网页上查找”框
点击鼠标中键或滚轮(只在谷歌浏览器测试版(只有英文版)中可用) 激活自动滚动。当您移动鼠标时,网页会根据鼠标的移动方向自动滚动。
Ctrl+F5 或 Shift+F5 重新加载当前页,但忽略缓存内容
按住 Alt 键,然后点击链接 下载链接
Ctrl+G 或 F3 查找与您在”在网页上查找”框中输入的内容相匹配的下一个匹配项
Ctrl+Shift+G 或 Shift+F3 查找与您在”在网页上查找”框中输入的内容相匹配的上一个匹配项
Ctrl+U 查看源代码
将链接拖动到书签栏 将链接加入书签
Ctrl+D 将当前网页加入书签
Ctrl++,或者按住 Ctrl 键并向上滚动鼠标滚轮 放大网页上的所有内容
Ctrl+-,或者按住 Ctrl 键并向下滚动鼠标滚轮 缩小网页上的所有内容
Ctrl+0 将网页上的所有内容都恢复到正常大小

文字快捷键

 

选中内容,然后按 Ctrl+C 键 将内容复制到剪贴板
将光标置于文本字段中,然后按 Ctrl+V 或Shift+Insert 键 从剪贴板粘贴当前内容
将光标置于文本字段中,然后按 Ctrl+Shift+V 键 从剪贴板粘贴当前内容的纯文本部分
选中文字字段中的内容,然后按 Ctrl+X 或Shift+Delete 键 删除内容并将其复制到剪贴板

我想你,但不会找你

我想你,但不会找你,来自张小娴。女人心,海底针。

有时候,你很想念一个人,但你不会打电话给他。打电话给他,不知道说甚麼好,还是不打比较好。

想念一个人,不一定要听到他的声音。听到了他的声音,也许就是另一回事。想像中的一切,往往比现实稍微美好一点。想念中的那个人,也比现实稍微温暖一点。思念好像是很遥远的一回事,有时却偏偏比现实亲近一点。

一个女人因为一个男人的离开而自寻短见,只有一个原因,就是除了他以外,她一无所有。拥有得愈多的人,愈舍不得死。一无所有的人,才会觉得活著没意思。

他不爱你,再过一万年之后也不爱你,你为甚麼还要为他痴迷,为他流泪?醒醒吧。

有些事情是不可以勉强的。恋爱是双程路,单恋也该有一条底线,到了底线,就是退出的时候。这条路行不通,你该想想另一条路,而不是在路口徘徊。这里不留人,自有留人处。

如果你开心和悲伤的时候,首先想到的,都是同一个人,那就最完美,如果开心的时候和悲伤的时候,首先想到的,不是同一个人,我劝你应该选择你想和他共度悲伤时刻的那一个,人生本来是苦多於乐。你的开心,有太多人可以和你分享,不一定要是情人,如果日子过得快乐,自己一人也很好,悲伤,却不是很多人可以和你分担。你愿意把悲伤告诉他,他才是你最想亲近和珍惜的人。

爱情里的所谓期限,都是用来延迟的。我很想不等你了,我却舍不得走。我知道我会老,我却舍不得放手。为甚麼要有期限?因为我担心我做不到。

爱情有生、老、病、死。爱情老了,生病了,治不好,爱情就会死。爱情要死,是时限到了。我们何必要恋恋不肯放手?万物有时序,你不可能一无所知,你只是希望把大限再延迟一点。花开花落,万物有时,你为甚麼不肯接受这是自然的定律?

离开之后,我想你不要忘记一件事:不要忘记想念我。想念我的时候,不要忘记我也在想念你。

就是喜欢这样,即使想你想到哭,我也不会去找你,我只是静静的想你。