1、+数据结构课程设计哈夫曼编码译码器 哈夫曼编码译码器a) 需求分析:一个完整的系统应具有以下功能: (l)I:初始化。从终端读入字符集大小n,及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmtree中。 (2)C:编码。利用已建好的哈夫曼树(如不在内存,则从文件hfmtree中读入),对文件tobetrans中的正文进行编码,然后将结果存入文件codefile中。 (3)D:编码。利用已建好的哈夫曼树将文件codefile中的代码进行译码,结果存入文件textfile中。 (4)P:印代码文件。将文件codefile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入
2、文件codeprint中。(5)T:印哈夫曼树。将已在内存中的哈夫曼树以直观的方式 (树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件treeprint中字符_ABCDEFGHIJKLM频度1866423223210321154757153220字符NOPQRSTUVWXYZ频度20561925051553010112212可以根据题目要求把程序划成5个模块,设计成菜单方式,每次执行一个模块后返回菜单。除了初始化(I)过程外,在每次执行时都经过一次读取磁盘文件数据。这是为了如果在程序执行后一直没有进行初始化(I)过程,为了能使后面的操作顺利进行,可以通过读取旧的数据来进行工作。
3、比如:如果程序的工作需要的字符集和权值数据是固定的,只要在安装程序时进行一次初始(I)化操作就可以了。在再次运行程序时,不管进行那项操作都可以把需要的数据读入到内存。b)概要设计本程序主要用到了三个算法。 (1)哈夫曼编码 在初始化(I)的过程中间,要用输入的字符和权值建立哈夫曼树并求得哈夫曼编码。先将输入的字符和权值存放到一个结构体数组中,建立哈夫曼树,将计算所得的哈夫曼编码存储到另一个结构体数组中。 (2)串的匹配 在编码(D)的过程中间,要对已经编码过的代码译码,可利用循环,将代码中的与哈夫曼编码的长度相同的串与这个哈夫曼编码比较,如果相等就回显并存入文件。 (3)二叉树的遍历 在印哈夫
4、曼树(T)的中,因为哈夫曼树也是二叉树,所以就要利用二叉树的先序遍历将哈夫曼树输出c)详细设计构造树的方法如下: 初始化:每个字符就是一个结点,字符的频度就是结点的权; 1、将结点按频度从小到大排序; 2、选取频度最小的 两个结点,以它们为儿子,构造出一个新的结点; 新结点的权值就是 它两个儿子的权值之和; 构造之后,从原来的结点序列里删除刚才选出的那两个结点,但同时将新生成的结点加进去; 3、如果结点序列里只剩下一个结点,表示构造完毕,退出。 否则回到第一步。编码: 上面已经生成了树,接着就该对该树进行编码了。 可以假定,对某个结点而言,其左孩子在当前阶段的编码为 0,右孩子的编码为 1。这
5、样就可以通过“树的遍历”的方式来生成 字符编码 对照表。 来到这里,基本上艰苦的已经完成了,对某个具体的字符串编码和解码就只是简单的“查表替换”的工作了。 解码: 解码也是个简单的查表-替换过程。如果利用该种编码发送字符串,则它的“字符编码”对应表也必须发送过去,不然对方是不知道怎么解码的。 对给出的一串编码,从左向右,将编码组合起来并查表,“一旦”找到有匹配的 字符,则马上将当前的 编码 替换为 对应的字符。 因为该编码是不会出现 “某一个字符的编码 是 另一个字符编码 的前缀” 这种情况的,也就是不会出现类似于“A 00 B 0010” 这样的情况,所以解码出来的字符串是唯一的,而且就是原
6、来进行编码的那一个。程序如下#include#include#includeusing namespace std;struct HuffmanNode /定义哈夫曼树各结点 int weight; /存放结点的权值,假设只考虑处理权值为整数的情况 int parent; /记录结点父亲位置,-1表示为根结点,否则表示为非根结点 int lchild,rchild; /分别存放该结点的左、右孩子的所在单元的编号;class HuffmanTree /建立哈夫曼树类private: HuffmanNode *Node; /哈夫曼树中结点的存储结构 char *Info; /用来保存各字符信息 i
7、nt LeafNum; /树中的叶子结点总数public: HuffmanTree(); /构造函数 HuffmanTree(); /析构函数 void Initialization(int WeightNum); /初始化函数:根据WeightNum个权值建立一棵哈夫曼树 void Encoder(); /编码函数:利用构造好的哈夫曼树对字符进行编码 void Decoder(); /译码函数:对二进制串进行译码 void Print(); /印文件函数:把已保存好的编码文件显示在屏幕 void TreePrinting(); /印哈夫曼树函数:将已在内存中的哈夫曼树以直观的方式显示在终端上
8、;/ 程序名:HuffmanTree.cpp/ 程序功能:实现哈夫曼树类的源文件(并用其来实现编/译码)/ 构造函数/ 函数功能:将结点指针初始化为NULLHuffmanTree:HuffmanTree() Node=NULL; /将树结点初始化为空 Info=NULL; /将字符数组初始化为空 LeafNum=0; /将叶子数初始化为0/ 析构函数/ 函数功能:将所有结点的空间释放/ 函数参数:无/ 参数返回值:无HuffmanTree:HuffmanTree() delete Node; /释放结点空间 delete Info; /释放字符存储空间/ 初始化函数/ 函数功能:从终端读入字符
9、集大小n,以及n个字符和n个权值,/ 建立哈夫曼树,并将它存放在文件hfmTree中./ 函数参数:int WeightNum表示代码个数/ 参数返回值:无 void HuffmanTree:Initialization(int WeightNum) /初始化 int i,j,pos1,pos2,max1,max2; Node=new HuffmanNode2*WeightNum-1; /WeightNum权值对应的哈夫曼树中的结点总数为2*WeightNum-1个 Info=new char2*WeightNum-1; for(i=0;iWeightNum;i+) cout请输入第i+1个字
10、符值; getchar(); /丢弃字符t与n Infoi=getchar(); /输入一个字符,主要是考虑输入空格而采用这种形式的 getchar(); coutNodei.weight; /输入权值 Nodei.parent=-1; /为根结点 Nodei.lchild=-1; /无左孩子 Nodei.rchild=-1; /无右孩子 for(i=WeightNum;i2*WeightNum-1;i+) /表示需做WeightNum-1次合并 pos1=-1; pos2=-1; /分别用来存放当前最小值和次小值的所在单元编号 max1=32767; /32767为整型数的最大值 max2=
11、32767; /分别用来存放当前找到的最小值和次小值 for(j=0;ji;j+) /在跟节点中选出权值最小的两个 if(Nodej.parent=-1) /是否为根结点 if(Nodej.weightmax1) /是否比最小值要小 max2=max1; /原最小值变为次小值 max1=Nodej.weight; /存放最小值 pos2=pos1; /修改次小值所在单元编号 pos1=j; /修改最小值所在单元编号 else if(Nodej.weightmax2) /比原最小值大但比原次小值要小 max2=Nodej.weight; /存放次小值 pos2=j; /修改次小值所在的单元编号
12、/for Nodepos1.parent=i; /修改父亲位置 Nodepos2.parent=i; Nodei.lchild=pos1; /修改儿子位置 Nodei.rchild=pos2; Nodei.parent=-1; /表示新结点应该是根结点 Nodei.weight=Nodepos1.weight+Nodepos2.weight; /for LeafNum=WeightNum; char ch; coutch; if(ch=y|ch=Y) ofstream fop; /以二进制方式打开hfmTree.dat文件,并当重新运行时覆盖原文件 fop.open(hfmTree.dat,i
13、os:out|ios:binary|ios:trunc); if(fop.fail() /文件打开失败 cout文件打开失败!n; fop.write(char*)&WeightNum,sizeof(WeightNum); /写入WeightNum for(i=0;iWeightNum;i+) /把各字符信息写入文件 fop.write(char*)&Infoi,sizeof(Infoi); flush(cout); for(i=0;i2*WeightNum-1;i+) /把个节点内容写入文件 fop.write(char*)&Nodei,sizeof(Nodei); flush(cout);
14、 fop.close(); /关闭文件 cout哈夫曼树已构造完成。n;/Initialization/ 编码函数/ 函数功能:利用已建立好的哈夫曼树(如不在内存,则从文件hfmTree中读入),/ 对文件ToBeTran中的正文进行编码,然后将结果代码存(传输)到文件CodeFilevoid HuffmanTree:Encoder() if(Node=NULL) /哈夫曼树不在内存,从文件hfmTree中读入 ifstream fip; /以二进制方式打开hfmTree.dat文件 fip.open(hfmTree.dat,ios:binary|ios:in); if(fip.fail()
15、/文件打开失败 cout文件打开失败!n; return; /结束本函数 fip.read(char*)&LeafNum,sizeof(LeafNum); /读取叶子数 Info=new charLeafNum; Node=new HuffmanNode2*LeafNum-1; for(int i=0;iLeafNum;i+) /读取字符信息 fip.read(char*)&Infoi,sizeof(Infoi); for(i=0;i2*LeafNum-1;i+) /读取结点信息 fip.read(char*)&Nodei,sizeof(Nodei); char *Tree; /用于存储需编码
16、内容 int i=0,num; char Choose; /让用户选择读取文件或重新输入需编码内容 coutChoose; if(Choose=1) /读取文件ToBeTran.txt ifstream fip1(ToBeTran.txt); if(fip1.fail() /文件不存在 cout文件打开失败!n; return; /结束本函数 char ch; int k=0; while(fip1.get(ch) k+; /计算CodeFile中代码长度 fip1.close(); Tree=new chark+1; ifstream fip2(ToBeTran.txt); k=0; whi
17、le(fip2.get(ch) Treek=ch; /读取文件内容,并存到Tree中 k+; fip2.close(); Treek=0; /结束标志 cout需编码内容为:; coutTreeendl; /if(Choose=1) else /Choose!=1,重新输入 string tree; /用于输入需编码内容,由于string类对象可以输入任意长度, /所以先利用这个对象输入,再转存在Tree中 cin.ignore(); cout请输入需要编码的内容(可输入任意长,结束时请按2下回车):n; getline(cin,tree,n); /输入任意长字符串, /getline以回车(
18、n)作为结束符,第一次按回车表示字符串结束,第二次按回车才开始输出。 while(treei!=0) i+; num=i; /计算tree长度 i=0; Tree=new charnum+1; while(treei!=0) /将tree中的字符转存到Tree中 Treei=treei; i+; Treei=0; /结束标志符 ofstream fop(CodeFile.dat,ios:trunc); /存储编码后的代码,并覆盖原文件 i=0; int k=0; char *code; code=new charLeafNum; /为所产生编码分配容量为LeafNum的存储空间 /因为不等长编
19、码中最长的编码一定不会超过要求编码的字符个数 while(Treek!=0) /对每一个字符编码 int j,start=0; for(i=0;iLeafNum;i+) if(Infoi=Treek) /求出该文字所在单元的编号 break; j=i; while(Nodej.parent!=-1) /结点j非树根 j=Nodej.parent; /非结点j的双亲结点 if(Nodej.lchild=i) /是左子树,则生成代码0 codestart+=0; else /是右子树,则生成代码1 codestart+=1; i=j; codestart=0; /置串结束符 for(i=0;ist
20、art/2;i+) /对二进制序列进行逆置 j=codei; codei=codestart-i-1; codestart-i-1=j; i=0; while(codei!=0) /存储代码 fopcodei; i+; k+; fop.close(); cout已编码!且存到文件CodeFile.dat中!nn; /Encode/ 译码函数/ 函数功能:利用已建好的哈夫曼树,对传输到达的CodeFile中的数据代码进行译码,/ 将译码结果存入文件TextFile中.void HuffmanTree:Decoder() int i=0,k=0; int j=LeafNum*2-1-1; /表示从
21、根结点开始往下搜索 char* BitStr; ifstream fip1(CodeFile.dat); /利用已建好的哈夫曼树将文件CodeFile中的代码进行译码 if(fip1.fail() /文件打开失败,还未编码 cout 请先编码!n; return; cout经译码,原内容为:; char ch; while(fip1.get(ch) k+; /计算CodeFile中代码长度 fip1.close(); BitStr=new chark+1; ifstream fip2(CodeFile.dat); k=0; while(fip2.get(ch) BitStrk=ch; /读取文
22、件内容 k+; fip2.close(); BitStrk=0; /结束标志符 if(Node=NULL) /还未建哈夫曼树 cout请先编码!n; return; ofstream fop(TextFile.dat); /将字符形式的编码文件写入文件CodePrin中 while(BitStri!=0) if(BitStri=0) j=Nodej.lchild; /往左走 else j=Nodej.rchild; /往右走 if(Nodej.rchild=-1) /到达叶子结点 coutInfoj; /输出叶子结点对应的字符 j=LeafNum*2-1-1; /表示重新从根结点开始往下搜索
23、fopInfoj; /存入文件 /if、 i+; /while fop.close(); coutn译码成功且已存到文件TextFile.dat中!nn;/Decoder/ 印文件代码函数/ 函数功能:将文件CodeFile以紧凑格式显示在终端上,/ 每行50个代码。同时将此字符形式的编码文件写入文件CodePrin中。/ 函数参数:无/ 参数返回值:无void HuffmanTree:Print() char ch; int i=1; ifstream fip(CodeFile.dat); /读取文件 ofstream fop(CodePrin.dat); /存储文件 if(fip.fail
24、() cout没有文件,请先编码!n; return; while(fip.get(ch) coutch; /读取文件内容 fopch; /存到文件中 if(i=50) /每行输出50个字符 coutendl; i=0; i+; coutendl; fip.close(); /关闭CodeFile.dat文件 fop.close(); /关闭CodePrin.dat文件/ 显示哈夫曼树函数/ 函数功能:将已在内存中的哈夫曼树以直观的方式(树或凹入表的形式)显示在终端上,/ 同时将此字符形式的哈夫曼树写入文件TreePrint中。void HuffmanTree:TreePrinting() i
25、f(Node=NULL) /未建立哈夫曼树 cout请先建立哈夫曼树!n; return; ofstream fop(TreePrint.dat); cout结点位置(权值) 编码 左孩子 编码右孩子(表示叶子)n; fop结点位置(权值) 编码 左孩子 编码LeafNum-1;i-) /输出哈夫曼树 couti(Nodei.weight)-1- Nodei.lchild(NodeNodei.lchild.weight)-0- Nodei.rchild(NodeNodei.rchild.weight)endl; fopi(Nodei.weight)-1- Nodei.lchild(NodeNodei.lchild.weight)-0- Nodei.rchild(NodeNodei.rchild.weight)=0;i-) couti:Nodei.weight(Infoi)-n; fopi:Nodei.weight(Infoi)-n; / 程序功能:主函数源文件/ 主函数int main() cout 欢迎使用哈夫曼码的编/译码系统!n; cout在此系统中可以进行以下操作:n; cout(1) 初始化(I);n; cout(2) 编码(E);n; cout(3) 译码(D);n; cout(4) 代码文件(P);n