哈夫曼树的编码与译码

合集下载

C++哈夫曼树编码和译码的实现

C++哈夫曼树编码和译码的实现

C++哈夫曼树编码和译码的实现⼀.背景介绍: 给定n个权值作为n个叶⼦结点,构造⼀棵⼆叉树,若带权路径长度达到最⼩,称这样的⼆叉树为最优⼆叉树,也称为哈夫曼树(Huffman Tree)。

哈夫曼树是带权路径长度最短的树,权值较⼤的结点离根较近。

⼆.实现步骤: 1.构造⼀棵哈夫曼树 2.根据创建好的哈夫曼树创建⼀张哈夫曼编码表 3.输⼊⼀串哈夫曼序列,输出原始字符三.设计思想: 1.⾸先要构造⼀棵哈夫曼树,哈夫曼树的结点结构包括权值,双亲,左右孩⼦;假如由n个字符来构造⼀棵哈夫曼树,则共有结点2n-1个;在构造前,先初始化,初始化操作是把双亲,左右孩⼦的下标值都赋为0;然后依次输⼊每个结点的权值2.第⼆步是通过n-1次循环,每次先找输⼊的权值中最⼩的两个结点,把这两个结点的权值相加赋给⼀个新结点,,并且这个新结点的左孩⼦是权值最⼩的结点,右孩⼦是权值第⼆⼩的结点;鉴于上述找到的结点都是双亲为0的结点,为了下次能正确寻找到剩下结点中权值最⼩的两个结点,每次循环要把找的权值最⼩的两个结点的双亲赋值不为0(i).就这样通过n-1循环下、操作,创建了⼀棵哈夫曼树,其中,前n 个结点是叶⼦(输⼊的字符结点)后n-1个是度为2的结点3.编码的思想是逆序编码,从叶⼦结点出发,向上回溯,如果该结点是回溯到上⼀个结点的左孩⼦,则在记录编码的数组⾥存“0”,否则存“1”,注意是倒着存;直到遇到根结点(结点双亲为0),每⼀次循环编码到根结点,把编码存在编码表中,然后开始编码下⼀个字符(叶⼦)4.译码的思想是循环读⼊⼀串哈夫曼序列,读到“0”从根结点的左孩⼦继续读,读到“1”从右孩⼦继续,如果读到⼀个结点的左孩⼦和右孩⼦是否都为0,如果是说明已经读到了⼀个叶⼦(字符),翻译⼀个字符成功,把该叶⼦结点代表的字符存在⼀个存储翻译字符的数组中,然后继续从根结点开始读,直到读完这串哈夫曼序列,遇到结束符便退出翻译循环四.源代码:1/***************************************2⽬的:1.根据输⼊的字符代码集及其权值集,3构造赫夫曼树,输出各字符的赫夫曼编码42.输⼊赫夫曼码序列,输出原始字符代码5作者:Dmego 时间:2016-11-116****************************************/7 #include<iostream>8#define MAX_MA 10009#define MAX_ZF 10010using namespace std;1112//哈夫曼树的储存表⽰13 typedef struct14 {15int weight; //结点的权值16int parent, lchild, rchild;//双亲,左孩⼦,右孩⼦的下标17 }HTNode,*HuffmanTree; //动态分配数组来储存哈夫曼树的结点1819//哈夫曼编码表的储存表⽰20 typedef char **HuffmanCode;//动态分配数组存储哈夫曼编码2122//返回两个双亲域为0且权值最⼩的点的下标23void Select(HuffmanTree HT, int n, int &s1, int &s2)24 {25/*n代表HT数组的长度26*/2728//前两个for循环找所有结点中权值最⼩的点(字符)29for (int i = 1; i <= n; i++)30 {//利⽤for循环找出⼀个双亲为0的结点31if (HT[i].parent == 0)32 {33 s1 = i;//s1初始化为i34break;//找到⼀个后⽴即退出循环35 }36 }37for (int i = 1; i <= n; i++)38 {/*利⽤for循环找到所有结点(字符)权值最⼩的⼀个39并且保证该结点的双亲为0*/40if (HT[i].weight < HT[s1].weight && HT[i].parent == 0)41 s1 = i;42 }43//后两个for循环所有结点中权值第⼆⼩的点(字符)44for (int i = 1; i <= n; i++)45 {//利⽤for循环找出⼀个双亲为0的结点,并且不能是s146if (HT[i].parent == 0 && i != s1)47 {48 s2 = i;//s2初始化为i49break;//找到⼀个后⽴即退出循环50 }51 }5253for (int i = 1; i <= n; i++)54 {/*利⽤for循环找到所有结点(字符)权值第⼆⼩的⼀个,55该结点满⾜不能是s1且双亲是0*/56if (HT[i].weight < HT[s2].weight && HT[i].parent == 0 && i!= s1)57 s2 = i;58 }5960 }6162//构造哈夫曼树63void CreateHuffmanTree(HuffmanTree &HT, int n)64 {65/*-----------初始化⼯作-------------------------*/66if (n <= 1)67return;68int m = 2 * n - 1;69 HT = new HTNode[m + 1];70for (int i = 1; i <= m; ++i)71 {//将1~m号单元中的双亲,左孩⼦,右孩⼦的下标都初始化为072 HT[i].parent = 0; HT[i].lchild = 0; HT[i].rchild = 0;73 }74for (int i = 1; i <= n; ++i)75 {76 cin >> HT[i].weight;//输⼊前n个单元中叶⼦结点的权值77 }78/*-----------创建⼯作---------------------------*/79int s1,s2;80for (int i = n + 1; i <= m; ++i)81 {//通过n-1次的选择,删除,合并来构造哈夫曼树82 Select(HT, i - 1, s1, s2);83/*cout << HT[s1].weight << " , " << HT[s2].weight << endl;*/84/*将s1,s2的双亲域由0改为i85 (相当于把这两个结点删除了,这两个结点不再参与Select()函数)*/86 HT[s1].parent = i;87 HT[s2].parent = i;88//s1,与s2分别作为i的左右孩⼦89 HT[i].lchild = s1;90 HT[i].rchild = s2;91//结点i的权值为s1,s2权值之和92 HT[i].weight = HT[s1].weight + HT[s2].weight;93 }94 }9596//从叶⼦到根逆向求每个字符的哈夫曼编码,储存在编码表HC中97void CreatHuffmanCode(HuffmanTree HT, HuffmanCode &HC, int n)98 {99 HC = new char*[n + 1];//分配储存n个字符编码的编码表空间100char *cd = new char[n];//分配临时存储字符编码的动态空间101 cd[n - 1] = '\0';//编码结束符102for (int i = 1; i <= n; i++)//逐个求字符编码103 {104int start = n - 1;//start 开始指向最后,即编码结束符位置105int c = i;106int f = HT[c].parent;//f指向结点c的双亲107while (f != 0)//从叶⼦结点开始回溯,直到根结点108 {109 --start;//回溯⼀次,start向前指向⼀个位置110if (HT[f].lchild == c) cd[start] = '0';//结点c是f的左孩⼦,则cd[start] = 0; 111else cd[start] = '1';//否则c是f的右孩⼦,cd[start] = 1112 c = f;113 f = HT[f].parent;//继续向上回溯114 }115 HC[i] = new char[n - start];//为第i个字符编码分配空间116 strcpy(HC[i], &cd[start]);//把求得编码的⾸地址从cd[start]复制到HC的当前⾏中117 }118delete cd;119 }120121//哈夫曼译码122void TranCode(HuffmanTree HT,char a[],char zf[],char b[],int n)123 {124/*125 HT是已经创建好的哈夫曼树126 a[]⽤来传⼊⼆进制编码127 b[]⽤来记录译出的字符128 zf[]是与哈夫曼树的叶⼦对应的字符(叶⼦下标与字符下标对应)129 n是字符个数,相当于zf[]数组得长度130*/131132int q = 2*n-1;//q初始化为根结点的下标133int k = 0;//记录存储译出字符数组的下标134int i = 0;135for (i = 0; a[i] != '\0';i++)136 {//for循环结束条件是读⼊的字符是结束符(⼆进制编码)137//此代码块⽤来判断读⼊的⼆进制字符是0还是1138if (a[i] == '0')139 {/*读⼊0,把根结点(HT[q])的左孩⼦的下标值赋给q140下次循环的时候把HT[q]的左孩⼦作为新的根结点*/141 q = HT[q].lchild;142 }143else if (a[i] == '1')144 {145 q = HT[q].rchild;146 }147//此代码块⽤来判断HT[q]是否为叶⼦结点148if (HT[q].lchild == 0 && HT[q].rchild == 0)149 {/*是叶⼦结点,说明已经译出⼀个字符150该字符的下标就是找到的叶⼦结点的下标*/151 b[k++] = zf[q];//把下标为q的字符赋给字符数组b[]152 q = 2 * n - 1;//初始化q为根结点的下标153//继续译下⼀个字符的时候从哈夫曼树的根结点开始154 }155 }156/*译码完成之后,⽤来记录译出字符的数组由于没有结束符输出的157时候回报错,故紧接着把⼀个结束符加到数组最后*/158 b[k] = '\0';159 }160//菜单函数161void menu()162 {163 cout << endl;164 cout << " ┏〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓┓" << endl;165 cout << " ┃★★★★★★★哈夫曼编码与译码★★★★★★★┃" << endl;166 cout << " ┃ 1. 创建哈夫曼树┃" << endl;167 cout << " ┃ 2. 进⾏哈夫曼编码┃" << endl;168 cout << " ┃ 3. 进⾏哈夫曼译码┃" << endl;169 cout << " ┃ 4. 退出程序┃" << endl;170 cout << " ┗〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓┛" << endl;171 cout << " <><注意:空格字符⽤'- '代替><>" << endl;172 cout << endl;173 }174void main()175 {176int falg;//记录要编码的字符个数177char a[MAX_MA];//储存输⼊的⼆进制字符178char b[MAX_ZF];//存储译出的字符179char zf[MAX_ZF];//储存要编码的字符180 HuffmanTree HT = NULL;//初始化树为空数181 HuffmanCode HC = NULL;//初始化编码表为空表182 menu();183while (true)184 {185int num;186 cout << "<><请选择功能(1-创建 2-编码 3-译码 4-退出)><>: ";187 cin >> num;188switch (num)189 {190case1 :191 cout << "<><请输⼊字符个数><>:";192 cin >> falg;193//动态申请falg个长度的字符数组,⽤来存储要编码的字符194/*char *zf = new char[falg];*/195 cout << "<><请依次输⼊" << falg << "个字符:><>: ";196for (int i = 1; i <= falg; i++)197 cin >> zf[i];198 cout << "<><请依次输⼊" << falg << "个字符的权值><>: ";199 CreateHuffmanTree(HT, falg);//调⽤创建哈夫曼树的函数200 cout << endl;201 cout << "<><创建哈夫曼成功!,下⾯是该哈夫曼树的参数输出><>:" << endl;202 cout << endl;203 cout << "结点i"<<"\t"<<"字符" << "\t" << "权值" << "\t" << "双亲" << "\t" << "左孩⼦" << "\t" << "右孩⼦" << endl;204for (int i = 1; i <= falg * 2 - 1; i++)205 {206 cout << i << "\t"<<zf[i]<< "\t" << HT[i].weight << "\t" << HT[i].parent << "\t" << HT[i].lchild << "\t" << HT[i].rchild << endl; 207 }208 cout << endl;209break;210case2:211 CreatHuffmanCode(HT, HC, falg);//调⽤创建哈夫曼编码表的函数212 cout << endl;213 cout << "<><⽣成哈夫曼编码表成功!,下⾯是该编码表的输出><>:" << endl; 214 cout << endl;215 cout << "结点i"<<"\t"<<"字符" << "\t" << "权值" << "\t" << "编码" << endl; 216for (int i = 1; i <= falg; i++)217 {218 cout << i << "\t"<<zf[i]<< "\t" << HT[i].weight << "\t" << HC[i] << endl; 219 }220 cout << endl;221break;222case3:223 cout << "<><请输⼊想要翻译的⼀串⼆进制编码><>:";224/*这样可以动态的直接输⼊⼀串⼆进制编码,225因为这样输⼊时最后系统会⾃动加⼀个结束符*/226 cin >> a;227 TranCode(HT, a, zf, b, falg);//调⽤译码的函数,228/*这样可以直接把数组b输出,因为最后有229在数组b添加输出时遇到结束符会结束输出*/230 cout << endl;231 cout << "<><译码成功!翻译结果为><>:" << b << endl;232 cout << endl;233break;234case4:235 cout << endl;236 cout << "<><退出成功!><>" << endl;237 exit(0);238default:239break;240 }241 }242243//-abcdefghijklmnopqrstuvwxyz244//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 1245//000101010111101111001111110001100100101011110110246247 }哈夫曼编码译码五.运⾏截图:。

哈夫曼编码和译码

哈夫曼编码和译码
11
哈夫曼树的构造算法: 哈夫曼树的构造算法: 个结点的一维数组, (1)初始化哈夫曼树的 )初始化哈夫曼树的2n-1个结点的一维数组, 个结点的一维数组 即将各结点中的各个域均置0; 即将各结点中的各个域均置 ; 个权值存放到一维数组的前n个单元中 (2)读入 个权值存放到一维数组的前 个单元中, )读入n个权值存放到一维数组的前 个单元中, 它们即为初始森林中的n个只含根结点的权值 个只含根结点的权值; 它们即为初始森林中的 个只含根结点的权值; (3)对森林中的二叉树进行合并,产生 个新 )对森林中的二叉树进行合并,产生n-1个新 结点,依次存放到一维数组的第n+1个开始的单元中, 个开始的单元中, 结点,依次存放到一维数组的第 个开始的单元中 在这个过程中要注意对每个结点双亲域, 在这个过程中要注意对每个结点双亲域,左右孩子 域以及权值的修改. 域以及权值的修改.
13
s1=maxval;s2= maxval;/ 设maxval为float类型最大值 ; ;/*设 ;/ 为 类型最大值 */ for(j =1;j<=i;j+ +) ( ; ; ) if (ht[j].parent = =0) /*判断是否为根结点 判断是否为根结点*/ 判断是否为根结点 if (ht[j].weight<s1) { s2=s1;s1= ht[j].weight; p2=p1;p1=j; } ; ; ; ; else if (ht[j].weight <s2) { s2= ht[j].weight; p2=j; } ; ; ht[p1].parent=i;ht[p2].parent=i; ; ; ht[i].lchild =p1;ht[i].rchild=p2; ; ; ht[i].weight= ht[pl].weight+ht[p2].weight; ; } return (ht);} ;

哈夫曼编码和译码

哈夫曼编码和译码

哈夫曼编码和译码哈夫曼编码是一种用于数据压缩的编码方法,通过根据字符出现的频率对字符进行编码,以便能够用较少的位数来表示出现频率较高的字符。

编码过程中,根据频率最低的字符构建一棵哈夫曼树,将字符的编码表示为树的路径。

而哈夫曼译码则是将经过哈夫曼编码后的数据解码成原始数据的过程。

在给定的文本中,首先需要统计每个字符的出现频率。

然后使用这些频率构建哈夫曼树。

构建哈夫曼树的过程中使用最小堆数据结构,通过不断合并两个最小频率的字符节点来构建树。

合并两个节点的过程中,创建一个新的节点来作为它们的父节点,并将其频率设置为两个子节点频率之和。

在构建树的过程中,赋予左子节点编码为0,右子节点编码为1。

构建完成哈夫曼树后,将每个字符的编码表示为从根节点到该字符节点的路径,例如,向左走的路径可以表示为0,向右走的路径可以表示为1。

为了能够正确译码,需要将字符及其对应的编码存储在一个编码表中。

对于给定的文本数据,应用哈夫曼编码后,会得到一个压缩后的编码字符串。

在哈夫曼译码过程中,从根节点开始遍历编码字符串的每一位,如果是0,则向左子节点移动,如果是1,则向右子节点移动,直到到达叶子节点。

找到叶子节点后,将对应的字符输出,并回到根节点,继续下一位的译码,直到编码字符串结束。

哈夫曼编码可以实现无损压缩,即通过译码过程能够还原出原始数据,而不会丢失任何信息。

它是一种广泛应用于数据压缩领域的有效方法,用于减小数据存储和传输的大小。

在实际应用中,哈夫曼编码被广泛应用于图像、音频和视频等多媒体数据的压缩中。

总结起来,哈夫曼编码是一种用于数据压缩的编码方法,通过根据字符出现的频率对字符进行编码,以便用较少的位数来表示出现频率较高的字符。

哈夫曼译码则是将经过编码后的数据解码成原始数据的过程。

它通过构建哈夫曼树和编码表,实现了高效的数据压缩和解压缩。

哈夫曼编码被广泛应用于数据存储和传输领域,特别是在多媒体数据的压缩中。

哈夫曼编码与译码完整版

哈夫曼编码与译码完整版

《数据结构》哈夫曼编码与译码实验报告题目:哈夫曼编码与译码班级:xxxx学号:xxxxxxxxxxx姓名:xxx完成时间:2012年12月19日一、程序总体结构主程序main显示系统时间showtime() 给用户提供选择方式chioce1()显示界面告诉用户程序名称show()打开文件进行加密openfile()退出程序输入电文进行加密input()将输入(文件中)的电文进行哈夫曼编码CrtHuffmanCode(ht,hc,n)统计输入(文件中)字母的出现频率CrW(data,w,count)【fcount(alldata,data,count)】输出每一个字母所对应的哈夫曼编码Printf(hc,n,data,alldata,count)将输入(文件中)的电文创建成哈夫曼树CrtHuffmantree(ht,w,n)对输入(文件中)的文字进行哈夫曼加密showall(hc,alldata,count,data,n)下面有几个不同的程序供选着参考:程序源代码:#include <iostream.h>#include <fstream.h>#include <string.h>#include <stdlib.h>typedef struct{char data;int weight;int parent,lchild,rchild;}HTNode,*HuffmanTree;typedef char * * HuffmanCode;void Select(HuffmanTree &HT,int n,int m){HuffmanTree p=HT;int tmp;for(int j=n+1;j<=m;j++){int tag1,tag2,s1,s2;tag1=tag2=32767;for(int x=1;x<=j-1;x++){ if(p[x].parent==0&&p[x].weight<tag1){ tag1=p[x].weight;s1=x;}}for(int y=1;y<=j-1;y++){ if(p[y].parent==0&&y!=s1&&p[y].weight<tag2){ tag2=p[y].weight;s2=y;}}if(s1>s2) //将选出的两个节点中的序号较小的始终赋给s1 { tmp=s1; s1=s2; s2=tmp;}p[s1].parent=j;p[s2].parent=j;p[j].lchild=s1;p[j].rchild=s2;p[j].weight=p[s1].weight+p[s2].weight;}}void HuffmanCoding(HuffmanTree &HT,int n,char *w1,int*w2) {int m=2*n-1;if(n<=1) return;HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); HuffmanTree p=HT;for(int i=1;i<=n;i++){ p[i].data=w1[i-1];p[i].weight=w2[i];p[i].parent=p[i].lchild=p[i].rchild=0;}for(;i<=m;i++){ p[i].weight=p[i].parent=p[i].lchild=p[i].rchild=0; }Select(HT,n,m);ofstream outfile; //生成hfmTree文件outfile.open("hfmTree.txt",ios::out);for (i=1;i<=m;i++){outfile<<HT[i].weight<<"\t"<<HT[i].parent<<"\t"<<HT[i].lchild<<"\t"<<HT[i].rchil d<<"\t"<<endl;}outfile.close();cout<<"初始化结果已保存在hfmTree文件中\n";}void ToBeTree() //将正文写入文件ToBeTree中{ofstream outfile;outfile.open("ToBeTree.txt",ios::out);outfile<<"THIS PROGRAM IS MYFAVORITE";outfile.close();}void Encoding(HuffmanTree &HT,int n) //编码{HuffmanCode HC;HC=(HuffmanCode)malloc((n+1)*sizeof(char *));char *cd;cd=(char *)malloc(n*sizeof(char));cd[n-1]='\0';for(int k=1;k<=n;k++){ int start=n-1;for(int c=k,f=HT[k].parent;f!=0;c=f,f=HT[f].parent){ if(HT[f].lchild==c) cd[--start]='0';else cd[--start]='1';}HC[k]=(char *)malloc((n-start)*sizeof(char));strcpy(HC[k],&cd[start]);}cout<<"输出哈夫曼编码:"<<endl;for(int h=1;h<=n;h++) //输出编码{ cout<<HT[h].data<<":";cout<<HC[h];cout<<" ";if (h%8==0) cout<<endl;}cout<<endl<<"输出正文编码:"<<endl;ToBeTree();//读取TOBETREE文件里的正文,并进行编码fstream infile;infile.open("ToBeTree.txt",ios::in);char s[80];while(!infile.eof()){infile.getline(s,sizeof(s));}infile.close();fstream outfile;outfile.open("CodeFile.txt",ios::out);int count=0;for (h=0;s[h]!='\0';h++){ for(k=1;k<=n;k++)if (s[h]==HT[k].data){ cout<<HC[k];cout<<" ";count++;outfile<<HC[k];break;}if (count%9==0) cout<<endl; //每输出7个换行}outfile.close();cout<<"\n编码结果已保存在文件CodeFile中."; cout<<endl;}void Decoding(HuffmanTree &HT,int n) //译码{int f=2*n-1;fstream infile;infile.open("CodeFile.txt",ios::in);char s[1000];while(!infile.eof()){infile.getline(s,sizeof(s));}infile.close();int i=0;int j=0;fstream outfile;outfile.open("TextFile.txt",ios::out);while(s[i]!='\0')while(HT[f].lchild!=0)//以f对应的节点的左孩子的值==0作为结束{if (s[j]=='0') f=HT[f].lchild;else f=HT[f].rchild;j++;}i=j;cout<<HT[f].data;outfile<<HT[f].data;}outfile.close();cout<<"\n译码结果已保存在文件TextFile中.";cout<<endl;}void Print() //印代码文件{ int count=0;fstream infile;infile.open("CodeFile.txt",ios::in);char s[1000];while(!infile.eof()){infile.getline(s,sizeof(s));for(int i=0;s[i]!='\0';i++){ cout<<s[i];count++;if (count%50==0) cout<<endl; //在终端上每行显示50个代码}}infile.close();cout<<endl;}char menu() //菜单函数{ cout<<"功能菜单如下:"<<endl;cout<<"* * * * * * * * * * * * * * * * * * * * *"<<endl;cout<<" I:初始化(Initialization) "<<endl;cout<<" E:编码(Encoding) "<<endl;cout<<" D:译码(Decoding) "<<endl;cout<<" P:印代码文件(Print) "<<endl;cout<<" Q:退出(Exit) "<<endl;cout<<"* * * * * * * * * * * * * * * * * * * * *"<<endl;cout<<"请输入功能字符:";char ch;cin>>ch;return ch;}{ int n;int Array[100];char cArray[100];HuffmanTree HT;cout<<"输入n个字符:";cin.getline(cArray,100);n=strlen(cArray);cout<<"一共"<<n<<"个字符.\n";cout<<"依次输入各个字符的权值:"<<endl;for (int i=1;i<=n;i++) cin>>Array[i];int tag;char x=menu();while(1){ switch (x){case 'I':HuffmanCoding(HT,n,cArray,Array);break; case 'E':Encoding(HT,n);break;case 'D':Decoding(HT,n);break;case 'P':Print();break;case 'Q':tag=0;cout<<"结束"<<endl;break; default:cout<<"你输入错误!"<<endl;}if(tag==0) break;cout<<"y(继续) or n(退出)"<<endl;char ch;cin>>ch;if (ch=='y'){ cout<<"请输入功能字符:";char c;cin>>c;x=c;}else exit(1);}}源程序:#include<iostream>#include<fstream>#include<iomanip>#include<vector>using namespace std;typedef struct //节点结构{char data; //记录字符值long int weight; //记录字符权重unsigned int parent,lchild,rchild;}HTNode,*HuffmanTree; //动态分配数组存储哈夫曼树typedef char * *HuffmanCode; //动态分配数组存储哈夫曼编码表void Select(HuffmanTree &HT,int i,int &s1,int &s2) //在HT[1...t]中选择parent不为0且权值最小的两个结点,其序号分别为s1和s2{s1=0;s2=0;int n1=30000,n2=30000;for(int k=1;k<=i;k++){if(HT[k].parent==0){if(HT[k].weight<n1){n2=n1; n1=HT[k].weight;s2=s1; s1=k;}elseif(HT[k].weight<n2){n2=HT[k].weight;s2=k;}}}}void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int n)//将要编码的字符串存入空树中{ifstream fin1("zifu.txt");ifstream fin2("weight.txt");if(n<=1)return;int m=2*n-1;int i;HT=new HTNode[m+1];char *zifu;int *weight;zifu= new char[n+1];weight=new int[n+1];for(i=1;i<=n;i++)//将待编码的字符放在zifu数组中{char ch;ch=fin1.get();zifu[i]=ch;}for(i=1;i<=n;i++)//将带编码字符对应的权值放在weight数组中{fin2>>weight[i];}for( i=1;i<=n;i++){HT[i].data=zifu[i];HT[i].weight=weight[i];}for(i=n+1;i<=m;i++){HT[i].data='@';}for(i=1;i<=m;i++){HT[i].parent=HT[i].lchild=HT[i].rchild=0;}for(i=n+1;i<=m;++i){int s1,s2;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=(HuffmanCode)malloc((n+1)*sizeof(char*));开辟一个求编码的工作空间char *cd;cd=(char *)malloc(n*sizeof(char));//开辟空间存放权值cd[n-1]='\0';for(i=1;i<=n;i++){int start=n-1;int c,f;for( c=i, f=HT[i].parent;f!=0;c=f,f=HT[f].parent)//从叶子到根逆向求编码{if(HT[f].lchild==c)cd[--start]='0';//若是左孩子编为'0'elsecd[--start]='1';//若是右孩子编为'1'}HC[i]=(char *)malloc((n-start)*sizeof(char)); //为第i个编码分配空间strcpy(HC[i],&cd[start]);}delete []cd; //释放工作空间}void printHuffmanTree(HuffmanTree HT,int n) //显示有n个叶子结点的哈夫曼树的编码表{ ofstream fout("hfmtree.txt"); //将对应字符的的哈弗曼树存入cout<<"NUM"<<" "<<"data"<<" "<<"weight"<<" "<<"parent"<<" "<<"lchild"<<" "<<"rchlid"<<endl;for(int i=1;i<=2*n-1;i++){fout<<HT[i].weight<<setw(3)<<HT[i].parent<<setw(3)<<HT[i].lchild<<setw(3) <<HT[i].rchild<<endl;cout<<i<<setw(5)<<HT[i].data<<setw(3)<<HT[i].weight<<setw(3)<<HT[i].pare nt<<setw(3)<<HT[i].lchild<<setw(3)<<HT[i].rchild<<endl;}}void printHuffmanCoding(HuffmanTree HT,HuffmanCode HC,int n)//输出字符的对应哈弗曼编码并存入code.txt文件{cout<<"Huffman code is:"<<endl;ofstream fout("code.txt");for(int i=1;i<=n;i++){cout<<HT[i].data<<" --> ";cout<<(HC[i])<<endl;fout<<(HC[i])<<endl;}}void code_file(HuffmanTree HT,HuffmanCode HC,int n)//对文件tobetran.txt进行编码,并将编码存入codefile文件中{ifstream fin("tobetran.txt");ofstream fout("codefile.txt");vector<char> a;char ch;while((ch=fin.get())!='*')a.push_back(ch);cout<<"待编码的字符串为:";for(int k=0;k<a.size();k++)cout<<a[k];cout<<endl;cout<<"\n编码结果:"<<endl;for(int i=0;i<a.size();i++){for(int j=1;j<=n;j++){if(a[i]==HT[j].data){fout<<HC[j];break;}}}fin.close();fout.close();}void Decoding(HuffmanTree HT,HuffmanCode HC,int n)//打开codefile文件并对文件内容进行译码{int const m=2*n-1;ifstream fin("codefile.txt");ofstream fout("textfile.txt");vector<char> a;for(char c;fin>>c;)a.push_back(c);int count=0;for(int k=0;k<a.size();k++){cout<<a[k];count++;if(count%50==0)cout<<endl;}int i=0;int p; //用p来记住m的值cout<<endl;cout<<"\n译码结果:"<<endl;while(i<a.size()){p=m; //从哈弗曼数的根开始遍历while(HT[p].lchild){if(a[i]=='1')p=HT[p].rchild;elsep=HT[p].lchild;i++;}fout<<HT[p].data;cout<<HT[p].data;}}void main(){int n;cout<<"输入权值个数:"; //设置权值数值cin>>n;printf("\n");HuffmanTree HT; //哈夫曼树HTHuffmanCode HC; //哈夫曼编码表HCHuffmanCoding(HT,HC,n); //进行哈夫曼编码printHuffmanCoding(HT,HC,n); //显示编码的字符printf("\n");code_file(HT,HC,n); //显示要编码的字符串,并把编码值显示出来Decoding(HT,HC,n); //译码并显示译码后的字符串printf("\n\n\n");system("pause");}主要程序代码://HuffmanCode1.h#ifndef HUFFMAMCODE_H#define HUFFMAMCODE_H#include<iostream>#include<fstream>using namespace std;struct HuffmanNode //定义哈夫曼树各结点{int weight;int parent;int lchild,rchild;int flag;};class HuffmanCode1 //哈夫曼编码类{public:char Info[100];int Start;char Leaf;};class HuffmanTree1 //建立哈夫曼树类{private:HuffmanNode *Node;public:int f;HuffmanCode1 *hf;HuffmanTree1();~HuffmanTree1();void TranslatedCode();void CodeHuf(HuffmanNode a[],HuffmanCode1 b[],int n);void CreateHfmTree(char Str[],int m[],int n);void TransCode(HuffmanCode1 b[],int n) ;void TranslateArtcle(HuffmanCode1 b[],int n) ;};#endif //HUFFMAMCODE_HHuffmanCode.cpp#include "iostream"#include<stdio.h>#include "math.h"#include "stdlib.h"#include"HuffmanCode1.h"#include<string>using namespace std;#define MAXDA TA 10000 //最长字符串#define MAXSIZE 150 //最多子叶数//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////第一部分功能(W)实现的代码$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ HuffmanTree1::HuffmanTree1(){ Node=NULL; } //将树结点初始化为空HuffmanTree1::~HuffmanTree1(){ delete[] Node; } //释放结点空间void HuffmanTree1::CreateHfmTree(char Str[],int m[],int n)//建立哈夫曼树{int i,j,m1,m2,x1,x2;HuffmanNode *HfmNode=new HuffmanNode[2*n-1];HuffmanCode1 *HfmCode=new HuffmanCode1[n];for(i=0;i<2*n-1;i++){HfmNode[i].weight=0;HfmNode[i].parent=0;HfmNode[i].flag=0;HfmNode[i].lchild=-1;HfmNode[i].rchild=-1;}for(i=0;i<n;i++){HfmNode[i].weight=m[i];HfmCode[i].Leaf=Str[i];}for(i=0;i<n-1;i++){m1=m2=32767;x1=x2=0;for(j=0;j<n+i;j++){if(HfmNode[j].weight<=m1&&HfmNode[j].flag==0){m2=m1;x2=x1;m1=HfmNode[j].weight;x1=j;}else if(HfmNode[j].weight<=m2&&HfmNode[j].flag==0){m2=HfmNode[j].weight;x2=j;}}HfmNode[x1].parent=n+i;HfmNode[x2].parent=n+i;HfmNode[x1].flag=1;HfmNode[x2].flag=1;HfmNode[n+i].weight=HfmNode[x1].weight+HfmNode[x2].weight;HfmNode[n+i].lchild=x1;HfmNode[n+i].rchild=x2;}CodeHuf(HfmNode,HfmCode,n);TransCode(HfmCode,n);//TranslateArtcle(HfmCode,n);hf=HfmCode; f=n;}void HuffmanTree1::CodeHuf(HuffmanNode a[],HuffmanCode1 b[],int n) //对哈夫曼树进行编码{HuffmanCode1 Hfd;int c,p;for(int i=0;i<n;i++){Hfd.Start=n-1;c=i;p=a[c].parent;while(p!=0){if(a[p].lchild==c)[Hfd.Start]='0';else[Hfd.Start]='1';Hfd.Start--;c=p;p=a[c].parent;}printf("%c :",b[i].Leaf);for(int j=Hfd.Start+1;j<n;j++){b[i].Info[j]=[j];printf("%c",[j]);}printf("\n");b[i].Start=Hfd.Start;}}void HuffmanTree1::TransCode(HuffmanCode1 b[],int n) //对文章进行翻译并保存{ifstream ifs("WData.txt");ofstream ofs("WCode.txt");char s[1000];int t=0;char ch;cout<<"************************************************************** *****************"<<endl;printf("报文的编码为:\n");while(ifs.get(ch)){if(ch!='\n')s[t]=ch;for(int i=0;i<n;i++){if(s[t]==b[i].Leaf)for(int j=b[i].Start+1;j<n;j++){printf("%c",b[i].Info[j]);ofs<<b[i].Info[j];}}t++;}printf("\n");printf("报文的编码已经保存在WCode.txt中\n");cout<<"*********************************************************** ********************"<<endl;}void HuffmanTree1::TranslateArtcle(HuffmanCode1 b[],int n) //将所译的码翻译成文章并保存{int t=0;ifstream ifs("WCode.txt");ofstream ofs("TransWData.txt");string s;getline(ifs,s);for(t=0;s[t]!='\0';t++);int l=0;int j=0;printf("报文的译码结果为:\n");while(l<t){while(j<n){int hu=b[j].Start+1;int k=0;while(hu<n){if(s[l]==b[j].Info[hu]){l++;hu++;k++;}else{break;}}if(hu==n){printf("%c",b[j].Leaf);ofs<<b[j].Leaf;j=0;break;}else{l=l-k;j++; continue;}}}printf("\n");printf("译码的结果已经保存到TransWData.txt中\n");cout<<"*********************************************************** ********************"<<endl;}void HuffmanTree1::TranslatedCode(){ifstream ifs("WData.txt");char str[1000];char Str[100];int i=0,j,m[100],h,k=0;int n=0;cout<<"*********************************************************** ********************"<<endl;printf("文件中提取的文章字符串是:\n");char ch;while(ifs.get(ch)){printf("%c",ch);if(ch!='\n'){str[n++]=ch;}}printf("\n");printf("字符串中共含有字符%d个\n",n);for(i=0;i<n;i++){j=0;h=0;while(str[i]!=str[j])j++;if(j==i){Str[k]=str[i];printf("字符%c出现",Str[k]);}elsecontinue;for(j=i;j<n;j++){if(str[i]==str[j])h++;}printf("%d次\n",h);m[k]=h;k++;}cout<<"*********************************************************** ********************"<<endl;printf("字符串中字符种类有%d种\n",k);cout<<"*********************************************************** ********************"<<endl;printf("每个字符对应的哈夫曼编码是:\n");CreateHfmTree(Str,m,k);cin.get();// printf("\n");}//main.cpp//#include"HuffmanCode1.h"//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////第二部分功能实现的代码$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ typedef struct //哈弗曼树节点的结构体{char info; //关联字符信息unsigned int weight; //每个节点的权职unsigned int parent, lchild, rchild;}HTNode,*HuffmanTree;typedef char **HuffmanCode; //存储哈弗曼编码void Select(HuffmanTree HT, int j,int &s1,int &s2){ //选择双亲节点为0,并且最小的两个子叶节点int i=1,m;while(HT[i].parent!=0)i++; //找第一个双亲节点为0的子叶结点for(s2=s1=i;i<j;i++){ /保证s1中的权值最小,s2次小if(HT[i].parent==0 && HT[i].weight<HT[s1].weight){s2=s1;s1=i;}else if(HT[i].parent==0 && HT[i].weight>=HT[s1].weight &&HT[i].weight<=HT[s2].weight)s2=i;while(HT[i].parent==0 && s1==s2){m=i;m++;while(HT[m].parent!=0)m++;s2=m;}}}void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int *w, int n, char *info){ //哈弗曼编码int i,m;HuffmanTree p;if(n<1) return;m = 2*n-1;HT = (HuffmanTree)malloc((m+1)*sizeof(HTNode));for(p=HT+1,i=1;i<=n;++i,++p,++w,++info){ //初始化所有已存在的子叶信息p->info = *info;p->weight = *w;p->parent = 0;p->lchild = 0;p->rchild = 0;}//forfor(; i<=m;++i,++p){ //构造所需要的过度根节点p->weight = 0;p->parent = 0;p->lchild = 0;p->rchild = 0;}//forfor(i=n+1;i<=m;++i){ //建立哈弗曼树int s1,s2;Select(HT,i-1,s1,s2);HT[s1].parent =i;HT[s2].parent =i;HT[i].lchild = s2;HT[i].rchild = s1;HT[i].weight = HT[s1].weight+HT[s2].weight;}//for//哈弗曼编码HC = (HuffmanCode)malloc((n+1)*sizeof(char *));char* cd = (char*)malloc(n*sizeof(char));cd[n-1] = '\0';for(i=1;i<=n;++i){int f;unsigned int c;int 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]);}//forfree(cd);}//HuffmanCoding//Y功能实现输出并保存字符串的二进制编码void CheckCoding(HuffmanTree HT, HuffmanCode HC, char *strcheck, int m,int k) { ofstream ofs("BCode.txt"); //查询哈弗曼编码信息int p;for(int i=0; i<m; i++){for(int j=1; HT[j].info != strcheck[i]; j++);cout<<HC[j]; //输出并保存字符串的二进制编码ofs<<HC[j];}cout<<endl;cout<<"字符串的二进制编码已经保存在“BCode.txt”中"<<endl;//cout<<"译码翻译得到的文章已保存在“Data.txt”中"<<endl;cout<<"************************************************************** *****************"<<endl;cout<<"各字符对应的编码为:"<<endl; //输出各字符对应的哈夫曼编码for( p=1;p<=k;p++){cout<<HT[p].info <<": "<<HC[p]<<endl;}}//CheckCoding//对键盘输入的二进制代码进行译码void HuffmanTranslateCoding(HuffmanTree HT, int n,char*c){ ofstream ofs("TransBData.txt"); //译码过程int m=2*n-1;int i,j=0;cout<<"译码翻译得到的文章已保存在“TransBData.txt”中"<<endl;cout<<"译码翻译得到的文章为:";while(c[j]!='\0'){i=m;while(HT[i].lchild && HT[i].rchild){if(c[j]=='0')i=HT[i].lchild;else if(c[j]=='1')i=HT[i].rchild;j++;}cout<<HT[i].info; //翻译成字符串并输出和保存ofs<<HT[i].info;}}//译码过程、、对"BCode.txt"的编码进行译码void HuffmanTranslateCoding2(HuffmanTree HT, int n){ ifstream ifs("BCode.txt");ofstream ofs("TransBData2.txt");string c;int m=2*n-1;int i,j=0;getline(ifs,c);cout<<"译码翻译得到的文章已保存在“TransBData2.txt”中"<<endl;cout<<"译码翻译得到的文章为:";while(c[j]!='\0'){i=m;while(HT[i].lchild && HT[i].rchild){if(c[j]=='0')i=HT[i].lchild;else if(c[j]=='1')i=HT[i].rchild;j++;}cout<<HT[i].info; //翻译成字符串并输出和保存ofs<<HT[i].info;}}void Menushow(){cout<<"||******************************************************************** ****||"<<endl;cout<<" || HuffmanCode and HUffmanTranslate System ||"<<endl;cout<<" || ***********哈夫曼编码/译码系统************* ||"<<endl;cout<<" || *************欢迎使用本系统**************** ||"<<endl;cout<<" || 东北电力大学信息工程学院计机093班兴趣小组||"<<endl;cout<<" || 制作人:范辉强(组长)李哲周兴宇||"<<endl;cout<<"||******************************************************************** ****||"<<endl;cout<<" ||在本系统中您可以进行如下操作:||"<<endl;cout<<" ||第一部分功能:||"<<endl;cout<<" || A :从文件提取字符串,然后对提取的字符串进行编码||"<<endl;cout<<" || B :根据W操作对“WCode.txt”里的二进制编码进行译码||"<<endl;cout<<" ||第二部分功能:||"<<endl;cout<<" || C :对您输入的字符串进行编码||"<<endl;cout<<" || D:对BCode.txt里的编码进行译码||"<<endl;cout<<" || E :对您输入的二进制编码进行译码||"<<endl;cout<<" ||第三部分功能:||"<<endl;cout<<" || F :退出本系统||"<<endl;cout<<"||******************************************************************** ****||"<<endl;cout<<" ||温馨提示:||"<<endl;cout<<" || 执行A,请将您的数据存储在同目录下名为“WData”文本文档里||"<<endl;cout<<" || 在执行C操作时务必在您输入的字符串后加上“#”||"<<endl;cout<<" || B与A是对应的B在A后运行||"<<endl;cout<<" || D/E与C是对应的,即B/C是根据C来进行译码的||"<<endl;cout<<" || 译码D/E应在编码C后才能进行||"<<endl;cout<<" ||*********************** Copyright by FanFan ********************||"<<endl;}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///main().cppint main(){int n=0,i=0,k=0,j,h,*w;FILE *fp;char ch2,str[MAXDA TA],choose,name[]="BData.txt";w=new int[MAXSIZE];char *info;char *strcheck=str;info=new char[MAXSIZE];char *ch=new char[MAXSIZE];HuffmanTree HT=new HTNode[MAXSIZE];HuffmanCode HC=NULL;HuffmanTree1 HuffmanNode;Menushow();while(1){cout<<endl;cout<<"************************************************************** *****************"<<endl;cout<<"*********************************************************** ********************"<<endl;cout<<"请输入您要进行的操作(W/F/B/C/Y/T)(不区分大小写):"<<endl;cin>>choose;cout<<"************************************************************** *****************"<<endl;switch(choose){case 'A':case 'a':HuffmanNode.TranslatedCode();break;case 'B':case 'b':HuffmanNode.TranslateArtcle(HuffmanNode.hf,HuffmanNode.f);case 'D':case 'd':HuffmanTranslateCoding2( HT,n);break;case 'E':case 'e'://进行译码操作cout<<"请您输入您要编译的二进制编码: "<<endl;cin>>ch;HuffmanTranslateCoding(HT,n,ch);break;case 'T':case 't'://退出系统return 0;case 'C':case 'c'://进行编码操作cout<<"请输入您要编码的字符串:"<<endl;//从键盘输入字符串存放在字符数组str[1000]中/*if ((fp=fopen(name,"w"))==NULL){cout<<"cannot open file"<<endl;exit(0);}*/fp=fopen(name,"w");ch2=getchar();//接收上一次键盘输入的换行符ch2=getchar();while(ch2!='#'){fputc(ch2,fp);str[n++]=ch2;putchar(str[n-1]);ch2=getchar();}putchar(10);fclose(fp);cout<<endl;cout<<"输入的数据已经保存在“BData.txt”中"<<endl;cout<<"*******************************************************************************"<<endl;cout<<"字符串中共含有字符"<<n<<"个"<<endl;cout<<"*********************************************************** ********************"<<endl;for(i=0;i<n;i++) // 统计从键盘输入的字符串的信息{j=0;h=0;while(str[i]!=str[j])j++;if(j==i){info[k]=str[i];cout<<"字符"<<info[k]<<"出现";}elsecontinue;for(j=i;j<n;j++){if(str[i]==str[j])h++;}cout<<h<<"次"<<endl;w[k]=h;k++;}cout<<"************************************************************** *****************"<<endl;cout<<"字符串中字符种类有"<<k<<"种"<<endl;cout<<"************************************************************** *****************"<<endl;HuffmanCoding( HT, HC, w, n,info);//对输入的字符串进行编码*strcheck=str[0];cout<<"您输入的字符串编码为:"<<endl;CheckCoding(HT,HC,strcheck,n,k);break;default:cout<<"对不起,您的输入不正确!请重新输入"<<endl;}//switch}//while}//main程序清单.cpp#include<iostream>#include<stdio.h>#include<stdlib.h>#include<string>#include"Hh1.h"using namespace std;FILE * f1=fopen("d:\\pra1.txt","r");FILE * f2=fopen("d:\\pra2.txt","w");FILE * f3=fopen("d:\\pra4.huf","w");int main(){init(SN); //初始化字符数据库// input(f1); //读入初始文件的字符//for(int i=0;forest[i]!=NULL;i++)cout<<forest[i]->c<<":"<<forest[i]->weight<<endl; //输出字符及出现次数// cout<<"出现字符种类"<<count<<endl; //输出字符种类//HFM huffman(count); //创建哈夫曼树实例// huffman.creat(); //创建哈夫曼树// count=0;huffman.hufcode(); //哈夫曼编码,此时为逆向//exchange(); //调整首尾对调哈夫曼编码// huffman.savewithhufcode(f1,f2); //用哈夫曼编码存储原文件//cout<<endl;cout<<"1.查看哈夫曼编码"<<endl;cout<<"2.哈夫曼解码"<<endl;cout<<"3.查看压缩率"<<endl;int choice;cin>>choice;while(choice>=1&&choice<=3){switch(choice){case 1:{for(i=0;hufNode[i].sig!=NULL;i++){cout<<"字符"<<hufNode[i].sig->c<<"的哈夫曼编码:"; //输出哈夫曼编码//for(int j=0;j<hufNode[i].size;j++)cout<<hufNode[i].code[j];cout<<endl;}cout<<"最大列数:"<<huffman.maxc()<<endl;break;}case 2:{fclose(f2);f2=fopen("d:\\pra2.txt","r");huffman.hufdecode(f2,f3); //哈夫曼解码//cout<<endl;break;}case 3:{compress(); //查看压缩情况//cout<<endl;}}cout<<"1.查看哈夫曼编码"<<endl;cout<<"2.哈夫曼解码"<<endl;cout<<"3.查看压缩率"<<endl;cin>>choice;}cout<<"*谢谢使用*"<<endl; //退出操作//return 0;}.h#include<iostream>using namespace std;struct signode{ //signode节点,哈夫曼树节点//char c; //字符//int weight; //权重//bool b; //文章中是否出现//。

哈夫曼编码和译码

哈夫曼编码和译码

哈夫曼编码和译码哈夫曼编码和译码是一种常用的数据压缩算法,它通过将出现频率较高的字符用较短的编码表示,从而减小数据的存储空间。

本文将介绍哈夫曼编码和译码的原理和应用。

哈夫曼编码是由美国数学家大卫·哈夫曼于1952年提出的一种编码方法。

它的基本思想是根据字符出现的频率来构建一棵二叉树,出现频率较高的字符位于树的较低层,而出现频率较低的字符位于树的较高层。

通过这种方式,出现频率较高的字符可以用较短的编码表示,而出现频率较低的字符则用较长的编码表示。

具体来说,哈夫曼编码的过程如下:首先,统计待编码的字符出现的频率,并根据频率构建一个字符频率表。

然后,根据字符频率表构建哈夫曼树,其中每个字符对应一个叶子节点,而非叶子节点的权值为其子节点权值之和。

接下来,通过遍历哈夫曼树,给每个字符赋予对应的编码,其中左子树路径上的编码为0,右子树路径上的编码为1。

最后,将编码后的字符序列存储起来,即完成了哈夫曼编码的过程。

哈夫曼译码是哈夫曼编码的逆过程,它通过已知的哈夫曼编码和字符频率表来将编码还原为原始的字符序列。

具体来说,哈夫曼译码的过程如下:首先,根据已知的字符频率表构建哈夫曼树。

然后,从根节点开始,根据编码的0和1进行遍历,直到叶子节点。

每次遍历到叶子节点时,将对应的字符输出,并重新回到根节点,继续下一次遍历,直到所有的编码都被译码为字符。

哈夫曼编码和译码在数据压缩领域有着广泛的应用。

由于哈夫曼编码可以将出现频率较高的字符用较短的编码表示,从而减小了数据的存储空间。

在传输大量文本、图像、音频等数据时,可以使用哈夫曼编码将数据进行压缩,从而减少传输的时间和带宽消耗。

而哈夫曼译码则可以将压缩后的数据还原为原始的数据,保证了数据的完整性和准确性。

除了数据压缩,哈夫曼编码和译码还有其他的应用。

在通信领域,哈夫曼编码可以用于错误检测和纠正,通过添加冗余信息来检测和纠正传输过程中的错误。

在图像和音频处理领域,哈夫曼编码可以用于图像和音频的压缩和解压缩,从而减小存储空间和提高传输效率。

哈夫曼树的编码和解码

哈夫曼树的编码和解码

哈夫曼树的编码和解码是哈夫曼编码算法的重要部分,下面简要介绍其步骤:
1. 编码:
哈夫曼编码是一种变长编码方式,对于出现频率高的字符使用较短的编码,而对于出现频率低的字符使用较长的编码。

具体步骤如下:(1)根据字符出现的频率,构建哈夫曼树。

频率相同的字符,按照它们在文件中的出现顺序排列。

(2)从哈夫曼树的叶子节点开始,从下往上逐步进行编码。

对于每个节点,如果该节点有左孩子,那么左孩子的字符编码为0,右孩子的字符编码为1。

如果该节点是叶子节点,则该节点的字符就是它的编码。

(3)对于哈夫曼树中的每个节点,都记录下它的左孩子和右孩子的位置,以便后续的解码操作。

2. 解码:
解码过程与编码过程相反,具体步骤如下:
(1)从哈夫曼树的根节点开始,沿着路径向下遍历树,直到找到一个终止节点(叶节点)。

(2)根据终止节点的位置信息,找到对应的字符。

(3)重复上述步骤,直到遍历完整个编码序列。

需要注意的是,哈夫曼编码是一种无损压缩算法,解压缩后的数据与原始数据完全相同。

此外,由于哈夫曼编码是一种变长编码方式,因此在解码时需要从根节点开始逐个解码,直到解码完成。

哈夫曼编码与解码

哈夫曼编码与解码

哈夫曼编码与解码
哈夫曼编码(Huffman coding)和哈夫曼解码(Huffman decoding)是一种用于数据压缩的技术,由美国计算机科学家 David A. Huffman 于 1952 年提出。

哈夫曼编码的基本思想是根据字符在文本中出现的频率来分配二进制编码的长度。

出现频率较高的字符将被分配较短的编码,而出现频率较低的字符将被分配较长的编码。

这样,通过使用较短的编码来表示常见字符,可以实现更有效的数据压缩。

哈夫曼编码的过程包括以下步骤:
1. 统计字符出现频率:对要编码的文本进行分析,统计每个字符出现的次数。

2. 构建哈夫曼树:根据字符出现频率构建一棵二叉树,其中频率较高的字符靠近树的根节点,频率较低的字符位于树的叶子节点。

3. 分配编码:从根节点开始,根据字符出现频率为每个字符分配二进制编码。

左子节点表示 0,右子节点表示 1。

4. 编码文本:将文本中的每个字符替换为其对应的哈夫曼编码。

哈夫曼解码是哈夫曼编码的逆过程,用于将已编码的数据还原为原始文本。

解码过程根据哈夫曼树的结构和编码规则,从编码中解析出原始字符。

哈夫曼编码与解码在数据压缩领域具有广泛的应用,例如图像、音频和视频压缩。

它通过有效地利用字符频率分布的不均匀性,实现了较高的压缩率,从而减少了数据传输和存储的开销。

需要注意的是,哈夫曼编码是一种无损压缩技术,意味着解码后可以完全还原原始数据。

但在实际应用中,可能会结合其他有损压缩技术来进一步提高压缩效果。

Huffman编码与译码 代码

Huffman编码与译码 代码

% 1 完成对输入的序列进行各个码元的概率统计;%完成对字符串中的各字符的统计,并列出其概率分布矩阵,返回pro矩阵%s:待编码序列,S:所含的码元序列function pro=getpro(s)pro=[];a=length(s);S=unique(s);b=length(S);c=zeros(1,b); %用以存放个序列中各个码元的个数;%/////////////////////////////////////////////%进行概率计算;for i=1:bfor j=1:aif S(i)==s(j)c(i)=c(i)+1;else continue;end;end;end;pro=c./a;disp(S);disp(pro);%完成对已知编码序列的译码,以及在改变码表中的某一位值得情况下,再一次译码,计算其误码率;%Codenumber:已编码序列;huffmantable:码表;Code2:各码元的码长;pro2:各码元的概率分布矩阵;%s:原始序列;decodenumber:译码序列;function decodenumber=huffmandecode(Codenumber,huffmantable,Code2,pro2,bit)mm=unique(bit); %码元序列mm=mm(pro2(2,:));[lx,ly]=size(huffmantable);LL=size(Codenumber,2);decodenumber=[];ZF=[Codenumber,-ones(1,max(Code2))];for j=1:length(bit)for i=1:lxk=Code2(i);while(ZF(1:k)==huffmantable(i,1:k))decodenumber=[decodenumber,mm(i)];ZF=ZF(k+1:end);endendenddisp('译码序列如下:');disp(decodenumber);disp('原始序列如下:');disp(bit);end %对于译码部分所用到的部分主要是编码时生成的码表以及huffmantree,在进行编码的时候,%通过筛选后的源字符串的字符序列的下标,与码表中的每行相对应的原则,遍历编码序列;%在遍历的时候,通过码表中各行的码长,控制遍历的长度,与每行中的码表进行比较,输出相对应的字符,即完成了译码。

哈弗曼编码与译码

哈弗曼编码与译码

一个完整的系统应具有以下功能:(1) I:初始化(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 中。

2.测试要求(1) 已知某系统在通信联络中只可能出现八种字符,其频率分别为0.05,0.29,0.07,0.08,0.14,0.23,0.03,0.11,试设计赫夫曼编码。

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

3.实现提示(1) 编码结果以文本方式存储在文件Codefile中。

(2) 用户界面可以设计为“菜单”方式:显示上述功能符号,再加上“Q”,表示退出运行Quit。

请用户键入一个选择功能符。

此功能执行完毕后再显示此菜单,直至某次用户选择了“Q”为止。

(3) 在程序的一次执行过程中,第一次执行I,D或C命令之后,赫夫曼树已经在存了,不必再读入。

每次执行中不一定执行I命令,因为文件hfmTree可能早已建好。

#include <string.h>#include <stdio.h>#include <stdlib.h>#include <malloc.h>typedef struct {char letter;float wt;}hfm,*hfmlist;//*************************全局变量************************************ unsigned int s1,s2,n;char choose;int DEPTH=0;char a[20];//***************Huffman树和Huffman编码的存储表示********************** typedef struct{unsigned int weight;unsigned int code;unsigned int parent,lchild,rchild;}HTNode, *HuffmanTree; //动态分配数组存储Huffman树typedef char * *HuffmanCode; //动态分配数组存储Huffman编码表//***************************初始化Huffman树***************************void select(HuffmanTree HT,int l){int a,b,c,j;s1=s2=1;a=b=1000;for(j=1;j<=l;j++){if(HT[j].code==0){c=HT[j].weight;if(c<a){b=a;a=c;s2=s1;s1=j;}else if(c<b){b=c;s2=j;}}//if}//forHT[s1].code=HT[s2].code=1;}//selectvoid HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC, hfmlist HL,unsigned int n){ //w存放n个权值(均>0),构造Huffman树HT,并求出n个字符的Huffman编码HC. unsigned int i,f,start,c,m;char*cd;if (n<=1) return;m =2*n-1;HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));for(i=1;i<=m;++i){HT[i].weight=HT[i].parent=HT[i].lchild=HT[i].rchild=HT[i].cod e=0;}for(i=1;i<=n;++i)HT[i].weight=HL[i].wt;for (i=n+1;i<=m;++i){select(HT,i-1);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=(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(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);}//*************************文件操作*****************************void save (char *p){FILE *fp;int i;printf("input the name:");if((fp=fopen(gets(a),"w+"))==NULL){printf("cannot open the file!\n");exit(0);}//iffor(i=0;p[i]!='\0';i++){if(fwrite(p+i,sizeof(char),1,fp)!=1){//fwrite函数写数据块printf("File write error!\n");exit(0);}//if}//forfclose(fp);}//save//************************编码与译码操作********************************** void Decoding(HuffmanTree HT,hfmlist HL,char*size,int n)//这个函数是用来译码的 {int i,j,k;char p[130];printf("请输入需要译码的代码:");gets(p);//接受需要译码的字符串i=2*n-1;k=0;for(j=0;p[j]!='\0';j++){if(p[j]=='0'){ if(HT[i].lchild!=0)i=HT[i].lchild;else{ size[k++]=HL[i].letter;i=2*n-1;j--;}}else{ if(HT[i].rchild!=0)i=HT[i].rchild;else{ size[k++]=HL[i].letter;i=2*n-1;j--;}}}//forif( p[j-1]=='0')size[k++]=HL[i].letter;else size[k++]=HL[i].letter;size[k]='\0';}//decodingvoid Encoding(HuffmanCode HC,hfmlist HL,char *coding,int n){ int i,j,t,k=0;char p[100];printf("请再次输入需要编码的文本:");gets(p);for(i=0;p[i]!='\0';i++){if((p[i]>='A'&&p[i]<='Z')||p[i]==' ')for(j=1;j<=n;j++)if(p[i]==HL[j].letter)for(t=0;HC[j][t]!='\0';t++)coding[k++]=HC[j][t];}//forcoding[k]='\0';}//encoding//************************打印操作**************************** void Print(FILE *fp1,FILE *fp2){//将文件fp1中的代码以紧凑格式显示在终端上,每行50个代码//同时将此字符形式的编码文件写入文件fp2中char c[200];int i;fgets(c,200,fp1);for(i=0;c[i]=='0'||c[i]=='1';i++){printf("%c",c[i]);fprintf(fp2,"%c",c[i]);if((i+1)/50*50==(i+1)){printf("\n");fprintf(fp2,"\n");}}//forprintf("\n");}//Printvoid PreOrderTraverse(HuffmanTree HT,int k,FILE *fp){//先序遍历并打印树,结果存放于fp中,k为根结点的下标int i;for(i=1;i<=DEPTH-1;i++)fprintf(fp," ");for(;i<=DEPTH;i++)fprintf(fp,"|------");fprintf(fp,"%d\n",HT[k].weight);if(HT[k].lchild!=0){DEPTH++;PreOrderTraverse(HT,HT[k].lchild,fp);DEPTH--;}if(HT[k].rchild!=0){DEPTH++;PreOrderTraverse(HT,HT[k].rchild,fp);DEPTH--;}if(HT[k].lchild==0&&HT[k].rchild==0)return;return;} //PreOrderTraversevoid TreePrinting(HuffmanTree HT,int n,FILE *fp){//打印树HT,结果存于文件fp中,n为树的叶子结点数int i,k;for(i=1;i<=2*n;i++)if(HT[i].parent==0){k=i;break;}//寻找根结点PreOrderTraverse(HT,k,fp);return;}//TreePrinting//**************************主函数*********************************int main(){FILE *fp1,*fp2,*fp3,*fp4,*fp5;HuffmanTree HT;HuffmanCode HC;hfmlist HL;unsigned int i,n;char *b,c[100],cha,ch;b=(char *)malloc(100*sizeof(char));printf("请选择操作:\n");printf("i:初始化(Initialization)\ne:编码(Encoding)\nd:译码(Decoding)\n");printf("p:印代码文件(Print)\nt:印哈夫曼树(Tree printing)\nq:退出(Quit)\n");b=(char *)malloc(100*sizeof(char));HL=(hfmlist)malloc(30*sizeof(hfm));//存储字符与权值,线性表长30printf("\n");printf("选择操作(i,e,d,p,t,q):");ch=getchar();getchar();printf("\n");do{switch(ch){case 'i':printf("正在进行初始化……\n");printf("输入字符长度:");scanf("%d",&n);getchar();printf("\n");HC=(HuffmanCode)malloc((n+1)*sizeof(char));HT=(HuffmanTree)malloc((2*n)*sizeof(HTNode));for(i=1;i<=n;i++){printf("请输入第%d个字符及权值(格式:字符权值):",i);scanf("%c %f",&(HL+i)->letter, &(HL+i)->wt);getchar();}//forif((fp5=fopen("hfmTree.txt","w"))==NULL){printf("cannot open file!\n");exit(0);}//iffor(i=1;i<=n;i++){fprintf(fp5,"第%d个字符及权值:\t\t\t",i);fprintf(fp5,"%c %f\n",(HL+i)->letter,(HL+i)->wt);}//forfclose(fp5);HuffmanCoding(HT,HC,HL,n);printf("\n已经将Huffman树存入文件hfmTree.txt中\n\n");printf("初始化完毕,请选择操作:\n");printf("i:重新初始化(Initialization)\ne:编码(Encoding)\nd:译码(Decoding)\n");printf("p:印代码文件(Print)\nt:印哈夫曼树(Tree printing)\nq:退出(Quit)\n\n");break;case 'e':printf("请输入编码,以#结尾:");if((fp2=fopen("ToBeTran.txt","w"))==NULL){printf("cannot open file\n");exit(0);}//ifcha=getchar();while(cha!='#'){fputc(cha,fp2);cha=getchar();}//whilegetchar();fclose(fp2);printf("已经把编码存入文件ToBeTran.txt中\n\n");Encoding(HC,HL,b,n);printf("文本的哈夫曼编码是:");printf("%s\n",b);printf("下面进行保存:");save(b);printf("编码完毕,请选择操作:\n");printf("i:重新初始化(Initialization)\ne:编码(Encoding)\nd:译码(Decoding)\n");printf("p:印代码文件(Print)\nt:印哈夫曼树(Tree printing)\nq:退出(Quit)\n");break;case 'd':Decoding(HT,HL,c,n);printf("输入要译码的编码:");printf("正在进行译码……\n");printf("%s\n",c);save(c);printf("已经将结果存入文件%s中\n",a);printf("译码完毕,请选择操作:\n");printf("i:重新初始化(Initialization)\ne:编码(Encoding)\nd:译码(Decoding)\n");printf("p:印代码文件(Print)\nt:印哈夫曼树(Tree printing)\nq:退出(Quit)\n");break;case 'p':for(i=1;i<=n;i++)printf("字符:%c\t\t\t译码:%s\n",HL[i].letter,HC[i]);if((fp1=fopen("CodeFile.txt","w"))==NULL){printf("cannot open the file!\n");exit(0);}//ifif((fp4=fopen("CodeFile.txt","r"))==NULL){printf("cannot open the file!\n");exit(0);}//ifPrint(fp4,fp1);printf("该代码文件已经存在并命名为CodePrin.txt\n"); fclose(fp4);fclose(fp1);break;case 't':printf("打印树存在于文件TreePrin中\n");if((fp3=fopen("TreePrin.txt","w"))==NULL){printf("cannot open file\n");exit(0);}TreePrinting(HT,n,fp3);fclose(fp3);break;case 'q':exit(0);break;default :exit(0);break;}//switchprintf("\n请选择操作(i,e,d,p,t,q):");ch=getchar();getchar();}while(ch!='q');//doreturn 0 ;}//main。

实验二哈夫曼树及哈夫曼编码译码的实现

实验二哈夫曼树及哈夫曼编码译码的实现

实验二哈夫曼树及哈夫曼编码译码的实现实验二:哈夫曼树及哈夫曼编码译码的实现(验证性、4学时) 一、实验目的和要求构建哈夫曼树及哈夫曼编码,输出哈夫曼树及哈夫曼编码,完成编码与译码的算法。

(1)掌握树的有关操作算法2)熟悉树的基本存储方法 ((3)学习利用树求解实际问题二、实验内容和原理定义哈夫曼树的存储结构;输入要编码的字符权重,根据权重建立哈夫曼树,并进行编码,最后输出哈夫曼编码。

三、实验环境硬件:(1)学生用微机(2)多媒体教室或远程教学(3)局域网环境软件:(1)Windows XP中文操作系统 (2)Turbo C 3.0 四、算法描述及实验步骤1( 算法描述(1) 建立叶子结点——由于每个叶子结点都有一个权重值,构造哈夫曼树的过程中每个分枝节点的权重值等于两个孩子结点权重值的和,所以应该有一个权重域和指向左右孩子的两个指针域;(2) 另外在建成哈夫曼树之后,为了求得每个叶子结点的哈夫曼编码,需要走一条从叶子结点到根结点的路径,而译码的过程是从根结点出发到叶子的不断匹配的过程,所以既需要知道孩子结点的信息,也需要知道双亲结点的信息,应该再有一个指向双亲结点的指针域。

(3) 构建哈夫曼编码——在已建好的哈夫曼树中从每个叶子结点开始沿双亲链域回退到根结点,每回退一步走过哈夫曼树的一个分枝得到一个哈夫曼编码值;由于每个叶子结点的哈夫曼编码是从根就诶点到相应叶子结点的路径上各分枝代码所组成的0、1序列,所以先得到的分枝代码为说求编码的低位码而后得到的为高位码。

2( 算法流程图构建哈夫曼树算法流程哈夫曼编码算法流程3( 代码#include<stdio.h>#define maxvalue 10000//定义最大权值常量#define maxnodenumber 100//定义结点最大数目常量 typedef struct{int weight;int parent,lchild,rchild; }htnode;typedef htnode *huffmantree;//定义哈夫曼树类型 htnodeht[maxnodenumber]; //定义静态三叉链表存储区数组#define maxbit 10//定义哈夫曼编码的最大长度 typedef struct//定义保存一个叶子结点哈夫曼编码的结构 {int bit[maxbit];//哈夫曼编码域为一维数组int start;//开始位置域为整型}hcodetype;//定义哈夫曼编码类型hcodetype cd[maxnodenumber];//定义存放哈夫曼编码的数组cd void crthuffmantree(int n);//建立哈夫曼树void gethuffmancode(int n);//哈夫曼编码void main (){int nodenum;printf("inputnodenum:"); scanf("%d",&nodenum);crthuffmantree(nodenum); gethuffmancode(nodenum); }void crthuffmantree(int n)//该算法对n个叶子结点建立哈夫曼树,权重值由键盘逐个输入{int i,j,m1,m2,k1,k2;//定义局部变量for(i=0;i<2*n-1;i++)//数组ht初始化{ht[i].weight=0;//权重初始化为0ht[i].parent=-1;//3个指针域初始化为-1,即NULL ht[i].lchild=-1;ht[i].rchild=-1;}for (i=0;i<n;i++)//读入n个叶子结点的权重值scanf("%d",&ht[i].weight); for(i=0;i<n-1;i++)//控制n-1趟生成新结点构造哈夫曼树 {m1=maxvalue;//预置最小权值变量为最大权值 m2=maxvalue;//预置次小权值变量为最大权值 k1=0;k2=0;//预置最小和次小权值结点位置为下标0处for (j=0;j<n+i;j++)//控制一趟中找处最小权值的结点if(ht[j].parent==-1&&ht[j].weight<m1){m2=m1;k2=k1;//若第j个结点权小于当前最小的m1改为次小的m1=ht[j].weight;k1=j;//并记下新的当前最小权值及位置}elseif(ht[j].parent==-1&&ht[j].weight<m2){m2=ht[j].weight;k2=j;}//否则若小于当前次小的m2则更新m2及其位置ht[k1].parent=n+i;//修改最小权值结点的双亲为刚生成的新结点ht[k2].parent=n+i;//修改次小权值结点的双亲刚好生成的新结点ht[n+i].weight=ht[k1].weight+ht[k2].weight;//填新生成结点的权重值ht[n+i].lchild=k1;//新生成结点的左孩子指针指向k1ht[n+i].rchild=k2;//新生成结点的右孩子指针指向k2}}void gethuffmancode(int n) /*对具有n个叶子结点的哈夫曼树ht,求所有叶子结点的哈夫曼编码并输出*/{int i,j,c,p; /*定义局部变量*/for(i=0;i<n;i++) /*定义存放哈夫曼编码的数组cd*/ {c=i;j=maxbit; /*为求一个结点的哈夫曼编码初始化c和j*/ do{j--; /*j指向bit中存放编码位的正确位置*/ p=ht[c].parent; /*p指向c 的双亲结点*/if(ht[p].lchild==c) /*如果c是p的左孩子*/cd[i].bit[j]=0; /*编码位上赋0*/elsecd[i].bit[j]=1; /*否则c是p的右孩子,编码位上赋1*/ c=p; /*更新c为p,为求下一个编码位做准备*/ }while(ht[p].parent!=-1); /*当未到达根结点继续做do循环*/ cd[i].start=j; /*求完一个叶子结点的哈夫曼编码时,记下编码开始位置*/}for(i=0;i<n;i++) /*输出n个叶子结点的哈夫曼编码*/{for(j=cd[i].start;j<maxbit;j++) /*逐位输出一个编码*/printf("%d",cd[i].bit[j]);printf("\n"); /*输出完一个哈夫曼编码后换行*/ }}五、调试过程一次编译二次编译三次编译六、实验结果七、总结(1) 深刻体会构建哈夫曼树的整个过程,对整体有个总的理解; (2) 复习以前所学C语言的一些语法,例如:for循环,if循环,while循环 (3) 理解哈夫曼的编码过程,编码思想(4) 此程序的不足之处在于在进行哈夫曼编码时未能对字符进行编制,有待改进。

哈夫曼编码和译码系统(附源代码)

哈夫曼编码和译码系统(附源代码)

实训报告题目:哈夫曼编码和译码系统院系:专业:姓名:学号:指导教师:日期:一.需求分析•二.概要设计(1)建立哈夫曼树、编码•(2)字符匹配•(3)哈夫曼树遍历• 3三.详细设计及编码实现•3四.流程图(1)总流程图75(2)编码实现流程图・16(3)译码实现流程图・17五.调试分析(1 )计算权值-18(1)生成哈夫曼树,建立编码表• 18 (3)将输入字符编码 19( 4 )输入新的字符串,进行译码 19( 5)输入新的二进制数将其译为字符 20六 . 系统维护 20 七.实验总结 20八. 源代码 21一.需求分析《1》问题描述:在传送电文时,人们总是希望传送时间尽可能短,这就是要求使电文代码长度尽可能短。

利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。

但是,这要求在发送端通过一个编码系统能够对待传输数据预先编码,在接收端将传来的数据进行译码。

对于双工信道(即可以双向传输信息的信道),每段都需要一个完整的编/译系统。

所以为这样的信息收发站写一个哈夫曼的编译码系统。

《2》打开一篇英文文章,统计该文章中每个字符出现的次数,然后以它们作为权值,对每一个字符进行编码,编码完成后再对其编码进行译码。

问题补充:1.从硬盘的一个文件里读出一段英语文章。

2.统计这篇文章中的每个字符出现的次数。

3.以字符出现字数作为权值,构建哈夫曼树,并将哈夫曼树的存储结构的初态和终态进行输出。

4.对每个字符进行编码并将所编码写入文件然后对所编码进行编译。

《3》这个哈夫曼编码译码主要是以英文字母输入进行编码与编译,编码译码过程由系统自动完成,人工操作部分就是电文的录入,和编译出来时的读操作。

.概要设计本程序主要用到了三个算法。

(1)哈夫曼树建立、编码在初始化(I)的过程中间,要用输入的字符和权值建立哈夫曼树并求得哈夫曼编码。

先将输入的字符和权值存放到一个结构体数组中,建立哈夫曼树,将计算所得的哈夫曼编码存储到另一个结构体数组中。

哈弗曼编码与译码

哈弗曼编码与译码

一个完整的系统应具有以下功能:(1) I:初始化〔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 中。

2.测试要求(1) 已知某系统在通信联络中只可能出现八种字符,其频率分别为,试设计赫夫曼编码。

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

3.实现提示(1) 编码结果以文本方式存储在文件Codefile中。

(2) 用户界面可以设计为“菜单”方式:显示上述功能符号,再加上“Q”,表示退出运行Quit。

请用户键入一个选择功能符。

此功能执行完毕后再显示此菜单,直至某次用户选择了“Q”为止。

(3) 在程序的一次执行过程中,第一次执行I,D或C命令之后,赫夫曼树已经在内存了,不必再读入。

每次执行中不一定执行I命令,因为文件hfmTree可能早已建好。

#include <string.h>#include <stdio.h>#include <stdlib.h>#include <malloc.h>typedef struct {char letter;float wt;}hfm,*hfmlist;//*************************全局变量************************************unsigned int s1,s2,n;char choose;int DEPTH=0;char a[20];//***************Huffman树和Huffman编码的存储表示********************** typedef struct{unsigned int weight;unsigned int code;unsigned int parent,lchild,rchild;}HTNode, *HuffmanTree; //动态分配数组存储Huffman树typedef char * *HuffmanCode; //动态分配数组存储Huffman编码表//***************************初始化Huffman树*************************** void select(HuffmanTree HT,int l){int a,b,c,j;s1=s2=1;a=b=1000;for(j=1;j<=l;j++){if(HT[j].code==0){c=HT[j].weight;if(c<a){b=a;a=c;s2=s1;s1=j;}else if(c<b){b=c;s2=j;}}//if}//forHT[s1].code=HT[s2].code=1;}//selectvoid HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC, hfmlist HL,unsigned int n){ //w存放n个权值〔均>0〕,构造Huffman树HT,并求出n个字符的Huffman编码HC. unsigned int i,f,start,c,m;char*cd;if (n<=1) return;m =2*n-1;HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));for(i=1;i<=m;++i){HT[i].weight=HT[i].parent=HT[i].lchild=HT[i].rchild=HT[i].code=0;} for(i=1;i<=n;++i)HT[i].weight=HL[i].wt;for (i=n+1;i<=m;++i){select(HT,i-1);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=(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(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);}//*************************文件操作*****************************void save (char *p){FILE *fp;int i;printf("input the name:");if((fp=fopen(gets(a),"w+"))==NULL){printf("cannot open the file!\n");exit(0);}//iffor(i=0;p[i]!='\0';i++){if(fwrite(p+i,sizeof(char),1,fp)!=1){//fwrite函数写数据块printf("File write error!\n");exit(0);}//if}//forfclose(fp);}//save//************************编码与译码操作********************************** void Decoding(HuffmanTree HT,hfmlist HL,char*size,int n)//这个函数是用来译码的{int i,j,k;char p[130];printf("请输入需要译码的代码:");gets(p);//接受需要译码的字符串i=2*n-1;k=0;for(j=0;p[j]!='\0';j++){if(p[j]=='0'){ if(HT[i].lchild!=0)i=HT[i].lchild;else{ size[k++]=HL[i].letter;i=2*n-1;j--;}}else{ if(HT[i].rchild!=0)i=HT[i].rchild;else{ size[k++]=HL[i].letter;i=2*n-1;j--;}}}//forif( p[j-1]=='0')size[k++]=HL[i].letter;else size[k++]=HL[i].letter;size[k]='\0';}//decodingvoid Encoding(HuffmanCode HC,hfmlist HL,char *coding,int n){int i,j,t,k=0;char p[100];printf("请再次输入需要编码的文本:");gets(p);for(i=0;p[i]!='\0';i++){if((p[i]>='A'&&p[i]<='Z')||p[i]==' ')for(j=1;j<=n;j++)if(p[i]==HL[j].letter)for(t=0;HC[j][t]!='\0';t++)coding[k++]=HC[j][t];}//forcoding[k]='\0';}//encoding//************************打印操作**************************** void Print(FILE *fp1,FILE *fp2){//将文件fp1中的代码以紧凑格式显示在终端上,每行50个代码//同时将此字符形式的编码文件写入文件fp2中char c[200];int i;fgets(c,200,fp1);for(i=0;c[i]=='0'||c[i]=='1';i++){printf("%c",c[i]);fprintf(fp2,"%c",c[i]);if((i+1)/50*50==(i+1)){printf("\n");fprintf(fp2,"\n");}}//forprintf("\n");}//Printvoid PreOrderTraverse(HuffmanTree HT,int k,FILE *fp){//先序遍历并打印树,结果存放于fp中,k为根结点的下标int i;for(i=1;i<=DEPTH-1;i++)fprintf(fp," ");for(;i<=DEPTH;i++)fprintf(fp,"|------");fprintf(fp,"%d\n",HT[k].weight);if(HT[k].lchild!=0){DEPTH++;PreOrderTraverse(HT,HT[k].lchild,fp);DEPTH--;}if(HT[k].rchild!=0){DEPTH++;PreOrderTraverse(HT,HT[k].rchild,fp);DEPTH--;}if(HT[k].lchild==0&&HT[k].rchild==0)return;return;} //PreOrderTraversevoid TreePrinting(HuffmanTree HT,int n,FILE *fp){//打印树HT,结果存于文件fp中,n为树的叶子结点数int i,k;for(i=1;i<=2*n;i++)if(HT[i].parent==0){k=i;break;}//寻找根结点PreOrderTraverse(HT,k,fp);return;}//TreePrinting//**************************主函数*********************************int main(){FILE *fp1,*fp2,*fp3,*fp4,*fp5;HuffmanTree HT;HuffmanCode HC;hfmlist HL;unsigned int i,n;char *b,c[100],cha,ch;b=(char *)malloc(100*sizeof(char));printf("请选择操作:\n");printf("i:初始化(Initialization)\ne:编码(Encoding)\nd:译码(Decoding)\n");printf("p:印代码文件(Print)\nt:印哈夫曼树(Tree printing)\nq:退出(Quit)\n");b=(char *)malloc(100*sizeof(char));HL=(hfmlist)malloc(30*sizeof(hfm));//存储字符与权值,线性表长30printf("\n");printf("选择操作(i,e,d,p,t,q):");ch=getchar();getchar();printf("\n");do{switch(ch){case 'i':printf("正在进行初始化……\n");printf("输入字符长度:");scanf("%d",&n);getchar();printf("\n");HC=(HuffmanCode)malloc((n+1)*sizeof(char));HT=(HuffmanTree)malloc((2*n)*sizeof(HTNode));for(i=1;i<=n;i++){printf("请输入第%d个字符及权值(格式:字符权值):",i);scanf("%c %f",&(HL+i)->letter, &(HL+i)->wt);getchar();}//forif((fp5=fopen("hfmTree.txt","w"))==NULL){printf("cannot open file!\n");exit(0);}//iffor(i=1;i<=n;i++){fprintf(fp5,"第%d个字符及权值:\t\t\t",i);fprintf(fp5,"%c %f\n",(HL+i)->letter,(HL+i)->wt);}//forfclose(fp5);HuffmanCoding(HT,HC,HL,n);printf("\n已经将Huffman树存入文件hfmTree.txt中\n\n");printf("初始化完毕,请选择操作:\n");printf("i:重新初始化(Initialization)\ne:编码(Encoding)\nd:译码(Decoding)\n");printf("p:印代码文件(Print)\nt:印哈夫曼树(Tree printing)\nq:退出(Quit)\n\n");break;case 'e':printf("请输入编码,以#结尾:");if((fp2=fopen("ToBeTran.txt","w"))==NULL){printf("cannot open file\n");exit(0);}//ifcha=getchar();while(cha!='#'){fputc(cha,fp2);cha=getchar();}//whilegetchar();fclose(fp2);printf("已经把编码存入文件ToBeTran.txt中\n\n");Encoding(HC,HL,b,n);printf("文本的哈夫曼编码是:");printf("%s\n",b);printf("下面进行保存:");save(b);printf("编码完毕,请选择操作:\n");printf("i:重新初始化(Initialization)\ne:编码(Encoding)\nd:译码(Decoding)\n");printf("p:印代码文件(Print)\nt:印哈夫曼树(Tree printing)\nq:退出(Quit)\n");break;case 'd':Decoding(HT,HL,c,n);printf("输入要译码的编码:");printf("正在进行译码……\n");printf("%s\n",c);save(c);printf("已经将结果存入文件%s中\n",a);printf("译码完毕,请选择操作:\n");printf("i:重新初始化(Initialization)\ne:编码(Encoding)\nd:译码(Decoding)\n");printf("p:印代码文件(Print)\nt:印哈夫曼树(Tree printing)\nq:退出(Quit)\n");break;case 'p':for(i=1;i<=n;i++)printf("字符:%c\t\t\t译码:%s\n",HL[i].letter,HC[i]);if((fp1=fopen("CodeFile.txt","w"))==NULL){printf("cannot open the file!\n");exit(0);}//ifif((fp4=fopen("CodeFile.txt","r"))==NULL){printf("cannot open the file!\n");exit(0);}//ifPrint(fp4,fp1);printf("该代码文件已经存在并命名为CodePrin.txt\n");fclose(fp4);fclose(fp1);break;case 't':printf("打印树存在于文件TreePrin中\n");if((fp3=fopen("TreePrin.txt","w"))==NULL) {printf("cannot open file\n");exit(0);}TreePrinting(HT,n,fp3);fclose(fp3);break;case 'q':exit(0);break;default :exit(0);break;}//switchprintf("\n请选择操作(i,e,d,p,t,q):");ch=getchar();getchar();}while(ch!='q');//doreturn 0 ;}//main。

哈夫曼编码与译码(附源码)

哈夫曼编码与译码(附源码)

建立Huffman树进行编码和译码的设计郝萌 1100300423 哈尔滨工业大学电脑科学与技术学院 1003104班摘要:建立一个简易的系统,对于给定的一篇英文文章,统计字符出现的概率,并根据概率建立Huffman树,利用Huffman编码对文章进行编码和译码。

掌握Huffman树的建立与应用,并进一步熟练掌握程序的设计流程。

关键词:Huffman树Huffman编码文章译码文件压缩解压缩1.引言:给定一篇文章,统计字符出现的概率,根据概率建立哈夫曼树,并进行哈夫曼编码,进而可以利用哈夫曼编码对文章进行编码与译码和文件压缩、解压缩等操作。

2.程序设计流程(1)文字表述开始进入功能选择界面,包含五种操作:1.读取文章并对字符编码,2.哈夫曼编码信息,3.文章编码,4.文章译码,5.文件压缩,6.文件解压缩,7.退出程序。

操作1:给定一篇文章,统计字符出现的概率,并根据概率建立Huffman树,并利用Huffman树对字符进行Huffman编码。

操作2:显示Huffman编码信息,包括字符,字符出现的概率,Huffman编码。

操作3:对文章进行译码,显示译码信息,并保存。

操作4:对文章进行译码,显示并保存。

操作5:对文件进行压缩,每7位二进制序列对应一个ASCII码。

操作6:对文件进行解压缩。

(2)流程图(3)程序数据要求及功能实现主界面1.读取文件并对字符进行编码2.哈夫曼编码信息3.文件编码(1)显示文件编码(2)保存文件编码4.文件译码(1)显示文章编码的译码(2)保存文章编码的译码5.文件压缩6.文件解压缩附:程序源代码/** File: HUFFMANFUNCTION.h* Author: Administrator** Created on 2011年12月19日, 下午6:19 */#ifndef HUFFMANFUNCTION_H#define HUFFMANFUNCTION_H#include <cstdlib>#include<iostream>#include<fstream>#include<math.h>#define max1 150#define max2 50#define max3 256using namespace std;class Htnote {public:char name; //字符名double weight; //权重int lchild; //左孩子int rchild; //右孩子int parent; //父亲Htnote() {weight = 0;lchild = -1;parent = -1;rchild = -1;}};class Name {public:int num; //字符出现的次数char pname; //字符名double lweight; //权值Name() {num = 0;lweight = 0;}};class GetName {public:char namef[max2];int n; //字符的种类int sum; //字符的总数Name letter[max1]; //存储字符信息的类的数组GetName() {sum = 0;n = 0;}void GetWeight()//得到字符的权值{for (int i = 0; i < n; i++) {letter[i].lweight = (double) letter[i].num / sum; //出现的次数除总数得到权值}}int ReadLetter() {ifstream input;cout << "请输入文件名:" << endl;cin >> namef;input.open(namef); //打开文件if (input.fail()) {cout << "该文件不存在!" << endl;return 0;}char ch;ch = input.get();letter[0].pname = ch;letter[0].num++;sum++;while (!input.eof()) {//读取文件中的所有字符int tag = 0;ch = input.get();for (int i = 0; i < n + 1; i++) {if (letter[i].pname == ch) {letter[i].num++;sum++;tag = 1;}}if (tag == 0) {n++;letter[n].pname = ch;letter[n].num++;sum++;}}sum--;input.close();GetWeight(); //得到字符权值}};class CodeNode//编码类{public:char ch; //存储字符char bits[max1]; //存储编码};class Function {public:GetName L;int fn; //定义哈夫曼数组大小Htnote HuffmanT[max3]; //哈夫曼数组CodeNode Code[max1]; //字符编码数组Function() {fn = 0;}void CharHuffmanTCoding()//编码功能实现{int i, f, c;char cd[L.n + 1]; //暂时存储编码的数组int start; //编码读取起始位置cd[L.n] = '\0';for (i = 0; i < L.n; i++) {Code[i].ch = HuffmanT[i].name; //字符信息start = L.n; //起始位置c = i;while ((f = HuffmanT[c].parent) >= 0) {if (HuffmanT[f].lchild == c)//如果为左孩子,为‘0’{cd[--start] = '0';} else//如果为右孩子,为‘1’{cd[--start] = '1';}c = f;}strcpy(Code[i].bits, &cd[start]); //将结果存入对应的编码数组中}}void OutputHuffmanTCode() {cout << "哈夫曼编码:" << endl;cout << "——————————————————————" << endl;cout << "字符\t权值\t\t哈夫曼编码" << endl;for (int i = 0; i < L.n; i++)//输出字符,权值,哈夫曼编码{cout << "——————————————————————" << endl;cout << HuffmanT[i].name << "\t" << HuffmanT[i].weight << "\t";cout << Code[i].bits;cout << endl;}cout << "——————————————————————" << endl;}void InitHT()//哈夫曼初始化{L.ReadLetter();fn = (L.n)*2 - 1;for (int i = 0; i < fn; i++) {if (i < L.n) {HuffmanT[i].name = L.letter[i].pname;HuffmanT[i].weight = L.letter[i].lweight;}}}void SelectMin(int m, int &p1, int &p2)//选择最小的两个节点{int i, j;double m1, m2;m1 = m2 = 1;p1 = p2 = -1;for (i = 0; i < m; i++) {if (HuffmanT[i].parent == -1 && HuffmanT[i].weight < m1)//找出为访问过的最小权值节点{m2 = m1;p2 = p1;m1 = HuffmanT[i].weight;p1 = i;} else if (HuffmanT[i].parent == -1 && HuffmanT[i].weight < m2)//找出为访问的权值第二小结点{m2 = HuffmanT[i].weight;p2 = i;}}}void CreatHT()//建立哈夫曼树{int i, p1, p2;InitHT();for (i = L.n; i < fn; i++) {SelectMin(i, p1, p2);HuffmanT[p1].parent = HuffmanT[p2].parent = i;HuffmanT[i].lchild = p1;HuffmanT[i].rchild = p2;HuffmanT[i].weight = HuffmanT[p1].weight + HuffmanT[p2].weight;}}int OutArticleCode()//显示文章编码{//文章编码操作ifstream input;input.open(f);if (input.fail()) {cout << "文件不存在!" << endl;return 0;}char ch;cout << "文章编码如下:" << endl;while (!input.eof()) {ch = input.get();for (int i = 0; i < L.n; i++) {if (Code[i].ch == ch)cout << Code[i].bits;}}cout << endl;input.close();}int SaveArticleCode()//保存文章编码{ofstream output;ifstream input;char namef1[max2];input.open(f);if (input.fail()) {cout << "该文件不存在!" << endl;return 0;}cout << "请输入保存文章编码的文件名:" << endl;cin >> namef1;output.open(namef1);char ch;while (!input.eof()) {ch = input.get();for (int i = 0; i < L.n; i++) {if (Code[i].ch == ch) {for (int j = 0; j < strlen(Code[i].bits); j++) {output.put(Code[i].bits[j]);}}}}input.close();output.close();cout << "保存完毕!" << endl;}int OutTransCode() {//文章译码操作ifstream input;char namef[max2];cout << "请输入保存文章编码的文件名:" << endl;cin >> namef;input.open(namef);if (input.fail()) {cout << "该文件不存在!" << endl;return 0;}char ch;ch = input.get();int c = 2 * L.n - 2;while (!input.eof()) {if (ch == '0')//遇0搜索左子树{if (HuffmanT[c].lchild >= 0) {c = HuffmanT[c].lchild;}if (HuffmanT[c].lchild == -1)//判断是否到叶子{cout << HuffmanT[c].name; //输出字符c = 2 * L.n - 2; //返回根节点}}if (ch == '1')//遇1搜索右子树{if (HuffmanT[c].rchild >= 0) {c = HuffmanT[c].rchild;}if (HuffmanT[c].rchild == -1)//判断是否到叶子{cout << HuffmanT[c].name; //输出字符c = 2 * L.n - 2; //返回根节点}}ch = input.get();}cout << endl;input.close();}int SaveTransCode() {//保存文章译码ofstream output;ifstream input;char namef[max2];char namef1[max2];cout << "请输入文章编码所在的文件名:" << endl;cin >> namef;input.open(namef);if (input.fail()) {cout << "该文件不存在!" << endl;return 0;}cout << "请输入保存文章译码的文件名:" << endl;cin >> namef1;output.open(namef1);char ch;ch = input.get();int c = 2 * L.n - 2;while (!input.eof()) {if (ch == '0') {if (HuffmanT[c].lchild >= 0) {c = HuffmanT[c].lchild;}if (HuffmanT[c].lchild == -1) {output.put(HuffmanT[c].name);c = 2 * L.n - 2;}}if (ch == '1') {if (HuffmanT[c].rchild >= 0) {c = HuffmanT[c].rchild;}if (HuffmanT[c].rchild == -1) {output.put(HuffmanT[c].name);c = 2 * L.n - 2;}}ch = input.get();}input.close();output.close();cout << "保存完毕!" << endl;}int FileCompression() {//文件压缩功能CreatHT();CharHuffmanTCoding();//保存编码ofstream output;ifstream input;char namef1[] = {"temp.txt"};input.open(f);if (input.fail()) {cout << "该文件不存在!" << endl;return 0;}output.open(namef1);char ch;while (!input.eof()) {ch = input.get();for (int i = 0; i < L.n; i++) {if (Code[i].ch == ch) {for (int j = 0; j < strlen(Code[i].bits); j++) {output.put(Code[i].bits[j]);}}}}input.close();output.close();//压缩文件ofstream File1;ifstream File2;char namef2[max2];cout << "请输入压缩后的文件名:" << endl;cin >> namef2;File1.open(namef2);File2.open(namef1);if (File2.fail()) {cout << "该文件不存在!" << endl;return 0;}char sh;char th;int i = 0;char j = '0';int count = 0;while (!File2.eof()) {sh = File2.get();if (i < 7 && sh != EOF) {count = count + (sh - '0') * pow(2, i);if (sh == '0') {j++;} else {j = '0';}i++;}if (i == 7) {th = count;File1.put(th);i = 0;count = 0;}if (sh == EOF) {th = count;File1.put(th);File1.put(j);i = 0;count = 0;}}File1.close();File2.close();remove(namef1);cout << "文件压缩完毕!" << endl;}int FileDecompression() {//文件解压缩//保存编码fstream output;fstream input;char namef2[max2];char namef1[] = {"temp.txt"};cout << "请输入待解压缩的文件名:" << endl;cin >> namef2;input.open(namef2, ios::in | ios::binary);output.open(namef1, ios::out | ios::binary);if (input.fail()) {cout << "该文件不存在!" << endl;return 0;}char ch;input.read(reinterpret_cast<char*> (&ch), sizeof (ch));char sh;char th = ch;input.read(reinterpret_cast<char*> (&ch), sizeof (ch));int count;char num;char t = '0';char p = '1';while (!input.eof()) {sh = th;th = ch;input.read(reinterpret_cast<char*> (&ch), sizeof (ch));count = sh;if (ch != EOF) {for (int i = 0; i < 7; i++) {num = count % 2 + '0';output.write(reinterpret_cast<char*> (&num), sizeof (num));count = count / 2;}}if (ch == EOF) {for (int i = 0; i < 7; i++) {num = count % 2 + '0';output.write(reinterpret_cast<char*> (&num), sizeof (num));count = count / 2;if (count == 0) {break;}}for (int i = 0; i < th - '0'; i++) {output.write(reinterpret_cast<char*> (&t), sizeof (t));}}}output.close();input.close();//解压文件fstream File1;fstream File2;char namef3[max2];cout << "请输入解压后的文件名:" << endl;cin >> namef3;File2.open(namef1, ios::in | ios::binary);File1.open(namef3);if (File2.fail()) {cout << "该文件不存在!" << endl;return 0;}cout << "解压后的文件内容为:" << endl;File2.read(reinterpret_cast<char*> (&ch), sizeof (ch));int c = 2 * L.n - 2;while (!File2.eof()) {if (ch == '0') {if (HuffmanT[c].lchild >= 0) {c = HuffmanT[c].lchild;}if (HuffmanT[c].lchild == -1) {cout << HuffmanT[c].name;File1.write(reinterpret_cast<char*> (&HuffmanT[c].name), sizeof (HuffmanT[c].name));c = 2 * L.n - 2;}}if (ch == '1') {if (HuffmanT[c].rchild >= 0) {c = HuffmanT[c].rchild;}if (HuffmanT[c].rchild == -1) {cout << HuffmanT[c].name;File1.write(reinterpret_cast<char*> (&HuffmanT[c].name), sizeof (HuffmanT[c].name));c = 2 * L.n - 2;}}File2.read(reinterpret_cast<char*> (&ch), sizeof (ch));}cout << endl;File2.close();File1.close();remove(namef1);cout << "解压完毕!" << endl;}};#endif /* HUFFMANFUNCTION_H */====================================================================== /** File: main.cpp* Author: Administrator** Created on 2011年12月13日, 上午9:09*/#include <iostream>#include "HUFFMANFUNCTION.h"using namespace std;/***/int main(int argc, char** argv){Function *a = new Function;while (1){//主界面显示cout << endl << endl << endl;cout << "<<==================功能选择===============>>" << endl;cout << " 【1】读取文章并对字符编码" << endl;cout << " 【2】哈夫曼编码信息" << endl;cout << " 【3】文章编码" << endl;cout << " 【4】文章译码" << endl;cout << " 【5】压缩文件" << endl;cout << " 【6】解压缩文件" << endl;cout << " 【7】退出程序" << endl;cout << "=========================================================" << endl << endl;char ch;cout << "====>>请选择功能:";cin >> ch;switch (ch){case '1'://读取文章并对字符编码{delete a;a = new Function;system("cls");a->CreatHT();a->CharHuffmanTCoding();cout << "操作完毕!" << endl;system("pause");system("cls");break;}case '2'://哈夫曼编码信息{system("cls");a->OutputHuffmanTCode();system("pause");system("cls");break;}case '3'://文章编码{system("cls");while (1){cout << endl;cout << "========>>1.显示文章编码" << endl;cout << "========>>2.保存文章编码" << endl;cout << "========>>3.返回上一界面" << endl;char ch1;cout << endl << "===>>请选择功能:";cin >> ch1;switch (ch1){case '1'://显示文章编码{system("cls");a->OutArticleCode();system("pause");system("cls");continue;}case '2'://保存文章编码{system("cls");a->SaveArticleCode();system("pause");system("cls");continue;}case '3'://返回上一界面{break;}default:{system("cls");cout << "输入错误,请重新输入!" << endl;continue;}}system("cls");break;}break;}case '4'://文章译码system("cls");while (1){cout << endl;cout << "========>>1.显示文章编码的译码" << endl;cout << "========>>2.保存文章编码的译码" << endl;cout << "========>>3.返回上一界面" << endl;char ch1;cout << endl << "===>>请选择功能:";cin >> ch1;switch (ch1){case '1'://显示文章编码的译码{system("cls");a->OutTransCode();system("pause");system("cls");continue;}case '2'://保存文章编码的译码{system("cls");a->SaveTransCode();system("pause");system("cls");continue;}case '3'://返回上一界面{break;}default:{system("cls");cout << "输入错误,请重新输入!" << endl;continue;}}system("cls");break;}break;case '5':{delete a;a = new Function;system("cls");a->FileCompression();system("pause");system("cls");continue;}case '6':{system("cls");a->FileDecompression();system("pause");system("cls");continue;}case '7':{return 0;}default:{system("cls");cout << "功能选择错误,请重新输入!" << endl;break;}}}return 0;}。

实验三 Huffman编码和译码

实验三 Huffman编码和译码

实验三 Huffman 编码和译码一、需求分析1、 用户必须先从键盘输入字符和相应字符的权值,在执行相应的操作。

2、演示程序以用户和计算机的对话方式执行,即在计算机显示“提示信息”后之后,由用户在键盘上输入演示程序中规定的运算命令;相应的输入数据和运算结果显示在其后。

3、程序执行的命令包括:(1)输入字符和相应的权值(2)程序运行,求解字符的编码并输出运算结果。

(3)输入一个字符串,生成码流。

4、测试数据A 0.3B 0.2C 0.05D 0.05E 0.1F 0.3ABFEDEFAD二、概要设计构造Huffman 树的方法——Huffman 算法根据给定的n 个权值{w1,w2,……wn},构造n 棵只有根 结点的二叉树,令初始权值为wj ;在森林中选取两棵根结点权值最小的树作左右子树,构造一棵新的二叉树,置新二叉树根结点权值为其左右子树根结点权值之和;在森林中删除这两棵树,同时将新得到的二叉树加入森林中重复上述两步,直到只含一棵树为止,这棵树即哈夫曼树。

哈弗曼树建成后,求编码需要从叶子节点出发走一条从叶子到根的路径。

译码需从走一条从根到叶子的路径。

本程序的设计思路是:1.建立huffman 编码树2.编码指定字符串3.译码指定码流为字符串三、详细设计1、定义头文件#include<iostream>#include<fstream>#include <string.h>#define MAX 10000using namespace std;int n=6;int m=2*n-1;2、元素类型、节点类型和辅助变量typedef struct{char* HfmCode;/*动态分配数组,存储哈夫曼编码*/char ch;}code, *HuffmanCode;typedef struct{unsigned int weight ; /* 用来存放各个结点的权值*/char ch;unsigned int parent, LChild,RChild ; /*指向双亲、孩子结点的指针*/}HTNode, * HuffmanTree;3,建立哈弗曼树void CreatHuffmanTree(HuffmanTree *ht , int *w, char *st,int n){ /* w存放已知的n个权值,构造哈夫曼树ht *//*由于是用数组的结构来存储树,故ht放的是数组的头指针*/int m,i;int s1,s2; /*在select函数中使用,用来存储最小权的结点的序号*/*ht=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); /*0号单元未使用*/for(i=1;i<=n;i++){/*1-n号放叶子结点,初始化*/(*ht)[i].weight = w[i];(*ht)[i].ch=st[i];(*ht)[i].LChild = 0;(*ht)[i].parent = 0;(*ht)[i].RChild = 0;}for(i=n+1;i<=m;i++){(*ht)[i].weight = 0;(*ht)[i].LChild = 0;(*ht)[i].parent = 0;(*ht)[i].RChild = 0;} /*非叶子结点初始化*/for(i=n+1;i<=m;i++) /*创建非叶子结点,建哈夫曼树*/{ /*在(*ht)[1]~(*ht)[i-1]的范围内选择两个parent为0且weight最小的结点,其序号分别赋值给s1、s2返回*/select(ht,i-1,&s1,&s2);(*ht)[s1].parent=i;(*ht)[s2].parent=i; /*设置这两个最小的权的结点的父结点为i*/(*ht)[i].LChild=s1;(*ht)[i].RChild=s2; /*设置这个父结点的子结点*/(*ht)[i].weight=(*ht)[s1].weight+(*ht)[s2].weight; /*设置这个父结点权*/ }}/*哈夫曼树建立完毕*/4.,select函数void select(HuffmanTree *ht,int n, int *s1, int *s2){ /*ht,为树所在数组的头指针,n叶子节点数,s1,s2,返回最小的两个序号*/int p1=MAX;int p2=MAX; /*p1, p2用来记录最小的两个权, 要求p1<p2*/int pn1=0;int pn2=0, i; /*pn1, pn2 用来记录这两个权的序号*/for(i=1;i<=n;i++)if((*ht)[i].weight<p1 && (*ht)[i].parent==0){/*如果当前结点的权比p1小,那么p2的权赋为p1的权, p1的权为当前权,同时修改n1, pn2*//*查找的结点要求没有父结点*/pn2=pn1;p2=p1;p1=(*ht)[i].weight;pn1=i;}elseif((*ht)[i].weight<p2 &&(*ht)[i].parent==0){/*如果当前结点的权比p2小(由第一个if,当前权不比p1小),那么p2的权赋为当前权,同时修改pn2*/p2=(*ht)[i].weight;pn2=i;}*s1=pn1;*s2=pn2; /*赋值返回*/}5求解哈佛曼编码void HuffmanCoding(HuffmanTree *ht, HuffmanCode *hc, int n)/*从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码*/{char *cd;int i;unsigned int c;int start;int p;*hc=(HuffmanCode)malloc((n+1)*sizeof(code)); /*分配n个编码的头指针*/cd=(char * )malloc(n * sizeof(char )); /*分配求当前编码的工作空间*/cd[n-1]='\0'; /*从右向左逐位存放编码,首先存放编码结束符*/for(i=1;i<=n;i++) /*求n个叶子结点对应的哈夫曼编码*/{(*hc)[i].ch=(*ht)[i].ch;start=n-1; /*初始化编码起始指针*//*通过c是current的意思,表示向根结点靠拢的当前结点,序号从i开始p是parent,始终指向c的父结点, p==0时c就是根结点*/for(c=i,p=(*ht)[i].parent; p!=0; c=p,p=(*ht)[p].parent) /*从叶子到根结点求编码*/ if( (*ht)[p].LChild == c)cd[--start]='0'; /*左分支标0*/elsecd[--start]='1'; /*右分支标1*//*for循环结点条件为*p=0表示c是根结点了,而每循环一次又由cd[--start]倒序记录了每一步是父结点的左或右结点,也就是形成了H编码,并放在cd中*/(*hc)[i].HfmCode=(char *)malloc((n-start)*sizeof(char)); /*为第i个编码分配空间*//*复制编码到对应的结点(当然是所有的需要编码的结点)*/strcpy((*hc)[i].HfmCode,&cd[start]);}free(cd);}6译码指定字符串为码流void Yima(HuffmanTree *ht, HuffmanCode *hc,char sh1[]) //将字符串转换为码流{ofstream fout("D:\input.txt");//定义输出文件流if(!fout){cout<<"文件打开失败"<<endl;return;}for(int i=0;i<strlen(sh1);i++){for(int j=1;j<=n;j++){if(sh1[i]==(*ht)[j].ch)fout<<(*hc)[j].HfmCode<<endl;}}fout.close();//关闭文件ifstream fin("D:\input.txt");// 定义输入文件流if(!fout){cout<<"文件打开失败"<<endl;return;}char shs[100];while(fin>>shs[i])cout<<shs[i];fin.close;}8,主函数部分int main(){HuffmanTree HT;HuffmanCode HC;int *w; /*各字符的权重*/char *st; /* 字符集,和st[i]和s[i]对应*/float wei; /* the weight of a element;*/char ch;//临时变量用来输入字符char str[20];w=(int *)malloc((n+1)*sizeof(int)); // 开辟空间st=(char *)malloc((n+1)*sizeof(int));//开辟空间for(int i=1;i<=n;i++) /*输入元素及其权重,存储在w,st中*/{cout<<"Input the"<<i<<" element and it's weight:" ;cin>>ch;cin>>wei;st[i]=ch;w[i]=wei;}CreatHuffmanTree(&HT,w,st,n); /*构造H树*/HuffmanCoding(&HT,&HC,n); /*根据H树,求每个字符的编码,放在HC中*/for(i=1;i<=n;i++) /*输出编码*/cout<<HC[i].ch<<" 编码是"<<HC[i].HfmCode<<endl;cout<<"请输入一个字符串"<<endl;cin>>str;Yima(&HT,&HC,str);return 0;}四、调试分析语法错误很容易发现和弄明白,但是遇到逻辑上的错误就很难调试出来。

简述哈夫曼编码译码过程

简述哈夫曼编码译码过程

简述哈夫曼编码译码过程哈夫曼编码是一种用于数据压缩的无损编码方法,它基于字符出现频率的统计信息,将频率较高的字符用较短的二进制编码表示,而将频率较低的字符用较长的二进制编码表示。

在对数据进行解码时,需要使用相同的编码表来将编码转换回原始数据。

哈夫曼编码的过程可以分为两个主要步骤:构建哈夫曼树和生成编码表。

下面将详细介绍每个步骤的实现过程。

构建哈夫曼树:1. 统计字符的频率:遍历要编码的数据,统计每个字符出现的频率。

2. 创建叶节点列表:将每个字符及其频率作为一个叶节点,构建一个列表。

3. 构建哈夫曼树:重复执行以下操作,直到只剩下一个节点:a. 从叶节点列表中选择两个频率最低的节点作为左右子节点。

b. 创建一个新的节点,其频率为左右子节点频率之和,并将其设为左右子节点的父节点。

c. 将新的父节点添加到叶节点列表中。

d. 从叶节点列表中删除选择的两个节点。

4. 哈夫曼树的根节点即为构建完成的树。

生成编码表:1. 遍历哈夫曼树:从根节点开始,遍历哈夫曼树的每个节点。

a. 若当前节点为叶节点,记录该节点所表示字符的编码路径。

b. 若当前节点有左子节点,将路径标记为0,并继续遍历左子节点。

c. 若当前节点有右子节点,将路径标记为1,并继续遍历右子节点。

2. 将每个字符与其对应的编码路径关系保存在编码表中。

哈夫曼编码的过程中,编码表的生成是非常重要的一步。

通过遍历哈夫曼树,可以确定每个字符的唯一编码,从而在进行译码时能够将编码路径按照对应的编码表转换为原始数据。

译码过程:1. 读取编码数据:将压缩后的二进制数据按位读取。

2. 解码树的遍历:从哈夫曼树的根节点开始,按照读取的二进制位(0或1)依次向左或向右遍历。

3. 判断节点类型:若当前节点为叶节点,表示已找到对应的字符,记录该字符并重新从根节点开始遍历。

4. 判断读取结束:若读取的二进制数据已经全部解码完毕,则译码结束;否则继续读取下一位二进制数据进行遍历。

数据结构 哈夫曼编码与译码

数据结构 哈夫曼编码与译码

数据结构哈夫曼编码与译码哈夫曼编码与译码是数据结构中的重要概念,它是一种可变长度编码的方法,用于压缩数据。

在本文中,我将详细介绍哈夫曼编码与译码的原理、步骤以及应用。

一、哈夫曼编码的原理哈夫曼编码是一种根据字符出现的频率来构建编码表的方法,使得频率较高的字符使用较短的编码,频率较低的字符使用较长的编码。

这样可以有效地减少数据的存储空间。

二、哈夫曼编码的步骤1. 统计字符频率:遍历待编码的文本,统计每个字符出现的频率。

2. 构建哈夫曼树:根据字符频率构建哈夫曼树。

首先将每个字符作为一个独立的树节点,然后合并频率最低的两个节点,生成一个新的节点,频率为这两个节点的频率之和。

重复此过程,直到所有节点都合并为一个根节点,得到哈夫曼树。

3. 生成编码表:从根节点开始遍历哈夫曼树,左子树路径为0,右子树路径为1,将路径上的0和1依次添加到对应字符的编码中,得到编码表。

4. 进行编码:根据编码表,将待编码的文本中的每个字符替换为对应的编码。

5. 完成编码:得到编码后的文本。

三、哈夫曼译码的步骤1. 根据编码表,将编码后的文本逐个字符地进行译码。

从根节点开始,根据字符是0还是1,选择左子树或右子树进行下一步操作。

2. 如果到达叶子节点,则找到对应的字符,并将该字符添加到译码结果中。

3. 重复上述步骤,直到译码结束,得到原始文本。

四、哈夫曼编码与译码的应用哈夫曼编码与译码广泛应用于数据压缩领域。

通过使用哈夫曼编码,可以大大减小数据的存储空间,提高数据传输的效率。

在图像、音频、视频等大数据文件的传输和存储中,哈夫曼编码被广泛使用。

总结:哈夫曼编码与译码是一种基于字符频率的编码方法,可以有效地压缩数据。

通过统计字符频率、构建哈夫曼树、生成编码表等步骤,可以实现对数据的编码和译码。

哈夫曼编码在数据压缩领域有着广泛的应用,可以大大减小数据的存储空间,提高数据传输的效率。

哈夫曼编码译码

哈夫曼编码译码

哈夫曼编码译码哈夫曼编码译码编码与译码是信息传输的两个基本环节,哈夫曼编码是一种基于数据压缩的编码方式,能够有效减小数据传输时所需的带宽和存储空间,广泛应用于图像、音频、视频等大数据的传输和存储中。

本文将从哈夫曼编码的定义、算法及其应用等三个方面进行探讨。

一、哈夫曼编码的定义哈夫曼编码,又称为变长编码或最优编码,是一种通过树形结构来实现数据压缩的编码方式。

其算法的基本思想是:对于频率高的字符,分配较短的编码;对于频率低的字符,分配较长的编码。

这样,可以减小数据传输时所需要的带宽和存储空间。

二、哈夫曼编码的算法哈夫曼编码的算法分为两个步骤:建立哈夫曼树和生成编码。

1. 建立哈夫曼树:首先将每个字符看作独立的一个节点,然后根据字符出现的频率来构建哈夫曼树。

具体的方法是:对于给定的一组字符,计算其出现的频率,并将其按照频率的大小进行排序。

然后将频率最小的两个字符合并为一个节点,该节点的频率为两个字符的频率之和,并将该节点插入到集合中。

重复此过程,直到集合中只剩下一个节点,即构建出了哈夫曼树。

2. 生成编码:从根节点开始,遍历树的所有节点(左子树编码为0,右子树编码为1),并记录所有节点对应的字符编码。

这样,每个字符就可以表示为一串01序列,其中该序列的长度越小,表示该字符在原数据中出现的频率越高。

三、哈夫曼编码的应用哈夫曼编码在数据压缩、网络传输、存储优化等领域都有广泛的应用。

1. 数据压缩:哈夫曼编码可将文件的体积减小到原来的三分之一左右,从而有效地减小文件的传输和存储所需要的空间。

2. 网络传输:在网络传输中,带宽和传输速率是非常宝贵的资源。

哈夫曼编码可以将传输的数据压缩到最小,从而减小网络传输的时间和成本。

3. 存储优化:在资源有限的设备上,如手机、PDA等,存储空间通常是一个非常重要的考虑因素。

使用哈夫曼编码技术,可以减小存储所需的空间,从而为设备的其他功能留出更多的空间。

总之,哈夫曼编码是一种非常重要的编码方式,广泛被应用于数据传输和存储中。

哈夫曼编码和译码运行与调试分析

哈夫曼编码和译码运行与调试分析

哈夫曼编码和译码运行与调试分析
哈夫曼编码和译码是一种常用的数据压缩算法,以下是其运行、调试和分析的步骤:
1. 运行:
- 首先,根据待压缩的数据,构建哈夫曼树。

- 根据哈夫曼树,生成编码表,即每个字符对应的二进制编码。

- 将待压缩数据按照编码表转换为二进制编码序列。

- 将二进制编码序列存储或传输,实现数据压缩。

2. 调试:
- 检查构建哈夫曼树的过程:确认权重和频率计算是否正确,检查树的构建过程是否存在错误。

- 核对编码表:确保每个字符的编码是否唯一且正确,检查编码表的生成过程是否存在错误。

- 验证压缩结果:按照编码表将压缩后的二进制编码序列还原为原始数据,与原始数据进行对比,确保压缩结果的正确性。

3. 分析:
- 计算压缩率:压缩率可以通过压缩前后数据的长度比值来计算,压缩率越高,表示算法的效果越好。

- 分析运行时间:可以根据输入数据的规模,通过调试工具或代码统计,对哈夫曼编码和译码的运行时间进行分析,评估其效率。

- 对比其他压缩算法:可以将哈夫曼编码与其他压缩算法进行对比,比如LZW算法或gzip 等,评估其压缩效果和性能。

以上是对哈夫曼编码和译码运行、调试和分析的一般步骤,具体实现还需根据算法的具体实现代码和环境进行适配。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

#include<stdio.h>
#include<stdlib.h>
#define n 4
#define m 2*n-1
#define maxval 32769
typedef struct
{
float weight;
int lchild,rchild,parent;
char ch;
}huftree;
typedef struct
{
char bits[n];
int start;
}codetype;
codetype code[n];
huftree tree[m];
void hufman(huftree tree[])//创建哈夫曼树{
int i,j,p1,p2;
char ch;
float small1,small2,f;
for(i=0;i<m;i++)//初始化所有结点
{
tree[i].parent=0;
tree[i].lchild=0;
tree[i].rchild=0;
tree[i].weight=0.0;
}
printf("请输入结点的权值:\n");
for(i=0;i<n;i++)//输入n个结点的权值{
scanf("%f",&f);
tree[i].weight=f;
}
printf("请输入结点的字符:\n");
scanf("%c",&ch);
for(i=0;i<n;i++)//输入n个结点的权值{
scanf("%c",&ch);
tree[i].ch=ch;
}
for(i=n;i<m;i++)//进行n-1次合并,产生n-1个新结点
{
p1=0,p2=0;
small1=maxval;
small2=maxval;
for(j=0;j<=i-1;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+1;//生成的新结点为最小权与次小权的双亲tree[p2].parent=i+1;
tree[i].lchild=p1+1;
tree[i].rchild=p2+1;
tree[i].weight=tree[p1].weight+tree[p2].weight;
}
}
void creathufcode(codetype code[])//由哈夫曼数构建哈夫曼编码
{
int i,c,p;
codetype cd;
for(i=0;i<n;i++)
cd.start=n;
c=i+1;
p=tree[i].parent;
while(p!=0)
{
cd.start--;
if(tree[p-1].lchild==c)
{
cd.bits[cd.start]='0';
}
else
cd.bits[cd.start]='1';
c=p;
p=tree[p-1].parent;
}
code[i]=cd;
}
}
void decode(codetype code[],huftree tree[])//依次读入电文,根据哈夫曼树译码{
int i,b;
int flag=-1;
i=m-1;
printf("请输入电文编码:");
scanf("%d",&b);
while(b!=flag)
{
if(b==0)
{
i=tree[i].lchild-1;
}
else
i=tree[i].rchild-1;
if(tree[i].lchild==0)//找到叶节点,输出对应字符
{
printf("译码后对应的字符:%c\n",tree[i].ch);
printf("译码后字符对应的权值:%f",tree[i].weight);
i=m-1;
}
scanf("%d",&b);
}
if(tree[i].lchild!=0)
printf("\n error\n"); }
void main()
{
hufman( tree);
creathufcode( code);
decode( code, tree);
}。

相关文档
最新文档