哈夫曼树解压与压缩
哈夫曼树解压与压缩
哈夫曼树的压缩与解压1.算法简要描述1.哈弗曼算法是根据给定的n个权值{w1,w2,w3.......wn},构造由n棵二叉树构成的深林F={T1,T2,。
Tn},其中每个二叉树Ti分别都是只含有一个权值wi的根结点,其左右子树为空〔i=1,,,,,,2〕。
2.在深林F中选取其根结点的权值最小的两棵二叉树,分别作其左右子树构造一颗新的二叉树,并置这棵新的二叉树根结点的权值为其左右子树的根结点之和。
3.从F中删去这两棵二叉树,同时刚新生成的二叉树参加到深林F中。
4.重复2,3,步骤,直至深林F中只含有一颗二叉树为止。
函数String EnCode(Char Type ch):表示哈夫曼树已存在,返回字符ch的编码。
函数LinkList<CharType>UnCode(String strCode):表示对哈夫曼树进展译码,返回编码前的字符序列。
根据算法可以看出,在具有n个结点权值的哈夫曼树的构造过程中,每次都是从F中删去两棵树,增加一棵树,即每次完毕后减少一棵树,经过n-1次处理后,F中就只剩下一棵树了。
另外,每次合并都要产生一个新的结点,合并n-1次后共产生了n-1个新结点,并且这n-1个新节点都是具有左右子树的分支结点。
如此最终得到的哈夫曼树中共有2n-1个结点,并且其中没有度为1的分支结点,最后一次产生的新结点就是哈夫曼树的根结点。
源代码中创建了一个哈夫曼树结点类,其中有数据成员weight,parent,leftChild,rightChild分别代表了权值,双亲,左孩子,右孩子。
在哈夫曼树类中有数据成员*nodes,*LeafChars,*LeafCharCodes,curPos,num,分别用来存储结点信息,叶结点字符信息,叶结点字符编码信息,译码时从根结点到叶结点路径的当前结点,叶结点个数。
哈夫曼树类中含有多个函数,有构造函数,析构函数等。
由函数HuffmanTree(CharType ch[],WeightType w[],int n)来构造由字符,权值,和字符个数构造哈夫曼树,在根据哈夫曼算法很容易实现哈夫曼类的函数以与构造函数。
利用哈夫曼编码实现压缩和解压缩
利用哈夫曼编码实现压缩和解压缩1.问题描述利用哈夫曼编码,实现压缩和解压缩的数据元素具有如下形式:结点:weight:存储结点的权值parent:是结点双亲在向量中的下标lchild:结点的左儿子向量下标rchild:结点右儿子向量下标bits:位串,存放编码ch:字符start:编码在位串中的起始位置文件操作记录:b:记录字符在数组中的位置count:字符出现频率(权值)lch、rch、parent:定义哈夫曼树指针变量bits[256]:定义存储哈夫曼编码的数组2.功能需求对于给定的一组字符,可以根据其权值进行哈夫曼编码,并能输出对应的哈夫曼树和哈夫曼编码;实现哈夫曼解码。
能够分析文件,统计文件中出现的字符,再对文件进行编码,实现文件的压缩和解压缩,能够对于文件的压缩,比例进行统计,能够打印文件。
3.实现要点(1)构造哈弗曼树过程中,首先将初始森林的各根结点的双亲和左、右儿子指针置-1;叶子在向量T的前n个分量中,构成初始森林的n个结点;对森林中的树进行n次合并,并产生n-1个新结点,依次放入向量T的第i个分量中。
(2)编码过程中,从叶子T[i]出发,利用双亲的指针找到双亲T[p];再根据T[p]的孩子指针可以知道T[i]是T[p]的左儿子还是右儿子,若是左儿子,则生成代码0,否则生成代码1;(3)在文件压缩和解压过程中,主要参考网上资料,每个步骤都基本理解,并注上了详细解析。
4.函数定义功能:输入权重,构造一棵哈弗曼树void huffman(hftree T){if(n<1 || n > m)return;int i,j,p1,p2;float small1,small2;//初始化cout<<"请输入叶子权重(5个):"<<endl;for(i=0; i<n; i++){T[i].parent = -1;T[i].lchild = T[i].rchild = -1;}//输入叶子权值for(i=0; i<n; i++){cin>>T[i].weight;}for(i=n; i<m; i++){p1 = p2 = -1;small1 = small2 = MAX_FLOAT;for(j=0; j<=i-1; j++){if(T[j].parent != -1)continue;if(T[j].weight < small1){small2 = small1;small1 = T[j].weight;p2 = p1;p1 = j;}else if(T[j].weight < small2){small2 = T[j].weight;p2 = j;}}T[p1].parent = T[p2].parent = i;T[i].parent=-1;T[i].lchild=p1;T[i].rchild=p2;T[i].weight=small1 + small2;}cout<<"创建成功!"<<endl;}功能:对哈弗曼树进行编码void encode(codelist codes, hftree T){int i,c,p,start;cout<<"请输入需要编码的字符(5个):"<<endl;for(i=0; i<n; i++){cin>>codes[i].ch;start=n;c=i;p=T[i].parent;while(p!=-1){start--;if(T[p].lchild==c) codes[i].bits[start]='0';else codes[i].bits[start]='1';c=p;p=T[p].parent;}codes[i].start=start;}cout<<"输入成功!:"<<endl;cout<<"编码表:"<<endl;for(int x=0; x<n; x++){cout<<codes[x].ch<<": ";for(int q=codes[x].start;q<n;q++) cout<<codes[x].bits[q];cout<<endl;}}函数功能:对哈弗曼树进行解码void decode(codelist codes,hftree T) {int i,c,p,b;int endflag;endflag=-1;i=m-1;while(cin>>b,b!=endflag){if(b==0) i=T[i].lchild;else i=T[i].rchild;if(T[i].lchild==-1){cout<<codes[i].ch;i=m-1;}}if(i!=m-1)cout<<"编码有错!\n";}功能:对文件进行压缩,统计压缩率void compress(){char filename[255],outputfile[255],buf[512];unsigned char c;long i,j,m,n,f;long min1,pt1,flength,length1,length2;double div;FILE *ifp,*ofp;cout<<"\t请您输入需要压缩的文件:";cin>>filename;ifp=fopen(filename,"rb");if(ifp==NULL){cout<<"\n\t文件打开失败!\n\n";return;}cout<<"\t请您输入压缩后的文件名:";cin>>outputfile;ofp=fopen(strcat(outputfile,".encode"),"wb");if(ofp==NULL){cout<<"\n\t压缩文件失败!\n\n";return;}flength=0;while(!feof(ifp)){fread(&c,1,1,ifp);header[c].count++; //字符重复出现频率+1flength++; //字符出现原文件长度+1}flength--;length1=flength; //原文件长度用作求压缩率的分母header[c].count--;for(i=0;i<512;i++){if(header[i].count!=0) header[i].b=(unsigned char)i;else header[i].b=0;header[i].parent=-1;header[i].lch=header[i].rch=-1; //对结点进行初始化}for(i=0;i<256;i++) //根据频率(权值)大小,对结点进行排序,选择较小的结点进树{for(j=i+1;j<256;j++){if(header[i].count<header[j].count){tmp=header[i];header[i]=header[j];header[j]=tmp;}}}for(i=0;i<256;i++) if(header[i].count==0) break;n=i; //外部叶子结点数为n个时,内部结点数为n-1,整个哈夫曼树的需要的结点数为2*n-1.m=2*n-1;for(i=n;i<m;i++) //构建哈夫曼树{min1=999999999; //预设的最大权值,即结点出现的最大次数for(j=0;j<i;j++){if(header[j].parent!=-1) continue;//parent!=-1说明该结点已存在哈夫曼树中,跳出循环重新选择新结点*/if(min1>header[j].count){pt1=j;min1=header[j].count;continue;}}header[i].count=header[pt1].count;header[pt1].parent=i; //依据parent域值(结点层数)确定树中结点之间的关系header[i].lch=pt1; //计算左分支权值大小min1=999999999;for(j=0;j<i;j++){if(header[j].parent!=-1) continue;if(min1>header[j].count){pt1=j;min1=header[j].count;continue;}}header[i].count+=header[pt1].count;header[i].rch=pt1; //计算右分支权值大小header[pt1].parent=i;}for(i=0;i<n;i++) //哈夫曼无重复前缀编码{f=i;header[i].bits[0]=0; //根结点编码0while(header[f].parent!=-1){j=f;f=header[f].parent;if(header[f].lch==j) //置左分支编码0{j=strlen(header[i].bits);memmove(header[i].bits+1,header[i].bits,j+1);//依次存储连接“0”“1”编码header[i].bits[0]='0';}else //置右分支编码1{j=strlen(header[i].bits);memmove(header[i].bits+1,header[i].bits,j+1);header[i].bits[0]='1';}}}fseek(ifp,0,SEEK_SET); //从文件开始位置向前移动0字节,即定位到文件开始位置fwrite(&flength,sizeof(int),1,ofp);fseek(ofp,8,SEEK_SET);buf[0]=0; //定义缓冲区,它的二进制表示00000000f=0;pt1=8;while(!feof(ifp)){c=fgetc(ifp);f++;for(i=0;i<n;i++){if(c==header[i].b) break;}strcat(buf,header[i].bits);j=strlen(buf);c=0;while(j>=8) //对哈夫曼编码位操作进行压缩存储{for(i=0;i<8;i++){if(buf[i]=='1') c=(c<<1)|1;else c=c<<1;}fwrite(&c,1,1,ofp);pt1++; //统计压缩后文件的长度strcpy(buf,buf+8); //一个字节一个字节拼接j=strlen(buf);}if(f==flength) break;}if(j>0) //对哈夫曼编码位操作进行压缩存储{strcat(buf,"00000000");for(i=0;i<8;i++){if(buf[i]=='1') c=(c<<1)|1;else c=c<<1;}fwrite(&c,1,1,ofp);pt1++;}fseek(ofp,4,SEEK_SET);fwrite(&pt1,sizeof(long),1,ofp);fseek(ofp,pt1,SEEK_SET);fwrite(&n,sizeof(long),1,ofp);for(i=0;i<n;i++){fwrite(&(header[i].b),1,1,ofp);c=strlen(header[i].bits);fwrite(&c,1,1,ofp);j=strlen(header[i].bits);if(j%8!=0) //若存储的位数不是8的倍数,则补0 {for(f=j%8;f<8;f++)strcat(header[i].bits,"0");}while(header[i].bits[0]!=0){c=0;for(j=0;j<8;j++) //字符的有效存储不超过8位,则对有效位数左移实现两字符编码的连接{if(header[i].bits[j]=='1') c=(c<<1)|1; //|1不改变原位置上的“0”“1”值else c=c<<1;}strcpy(header[i].bits,header[i].bits+8); //把字符的编码按原先存储顺序连接fwrite(&c,1,1,ofp);}}length2=pt1--;div=((double)length1-(double)length2)/(double)length1; //计算文件的压缩率fclose(ifp);fclose(ofp);printf("\n\t压缩文件成功!\n");printf("\t压缩率为 %f%%\n\n",div*100);return;}函数功能:对文件解压缩void uncompress(){char filename[255],outputfile[255],buf[255],bx[255];unsigned char c;long i,j,m,n,f,p,l;long flength;FILE *ifp,*ofp;cout<<"\t请您输入需要解压缩的文件:";cin>>filename;ifp=fopen(strcat(filename,".encode"),"rb");if(ifp==NULL){cout<<"\n\t文件打开失败!\n";return;}cout<<"\t请您输入解压缩后的文件名:";cin>>outputfile;ofp=fopen(outputfile,"wb");if(ofp==NULL){cout<<"\n\t解压缩文件失败!\n";return;}fread(&flength,sizeof(long),1,ifp); //读取原文件长度,对文件进行定位fread(&f,sizeof(long),1,ifp);fseek(ifp,f,SEEK_SET);fread(&n,sizeof(long),1,ifp);for(i=0;i<n;i++){fread(&header[i].b,1,1,ifp);fread(&c,1,1,ifp);p=(long)c; //读取原文件字符的权值header[i].count=p;header[i].bits[0]=0;if(p%8>0) m=p/8+1;else m=p/8;for(j=0;j<m;j++){fread(&c,1,1,ifp);f=c;itoa(f,buf,2); //将f转换为二进制表示的字符串f=strlen(buf);for(l=8;l>f;l--){strcat(header[i].bits,"0");}strcat(header[i].bits,buf);}header[i].bits[p]=0;}for(i=0;i<n;i++) //根据哈夫曼编码的长短,对结点进行排序{for(j=i+1;j<n;j++){if(strlen(header[i].bits)>strlen(header[j].bits)){tmp=header[i];header[i]=header[j];header[j]=tmp;}}}p=strlen(header[n-1].bits);fseek(ifp,8,SEEK_SET);m=0;bx[0]=0;while(1) //通过哈夫曼编码的长短,依次解码,从原来的位存储还原到字节存储{while(strlen(bx)<(unsigned int)p){fread(&c,1,1,ifp);f=c;itoa(f,buf,2);f=strlen(buf);for(l=8;l>f;l--) //在单字节内对相应位置补0{strcat(bx,"0");}strcat(bx,buf);}for(i=0;i<n;i++){if(memcmp(header[i].bits,bx,header[i].count)==0) break;}strcpy(bx,bx+header[i].count);c=header[i].b;fwrite(&c,1,1,ofp);m++; //统计解压缩后文件的长度if(m==flength) break; //flength是原文件长度}fclose(ifp);fclose(ofp);cout<<"\n\t解压缩文件成功!\n";if(m==flength) //对解压缩后文件和原文件相同性比较进行判断(根据文件大小)cout<<"\t解压缩文件与原文件相同!\n\n";else cout<<"\t解压缩文件与原文件不同!\n\n";return;}5、总结和体会本次大作业与C++大作业有所不同,主要是利用构造数据结构解决问题,C++大作业主要体现类和文件读写功能。
c++哈夫曼树的文件压缩解压程序全部代码及设计报告
#include <iostream>#include <fstream>#include <queue> //队列容器using namespace std;const int leaf = 256; //最多可能出现的不同字符数const long MAX = 99999999; //表示无穷大typedef struct HTnode{long weight; //记录结点的权值int parent; //记录结点的双亲结点位置int lchild; //结点的左孩子int rchild; //结点的右孩子int *code; //记录该结点的huffman编码int codelen; //记录该结点huffman编码的长度HTnode(){weight = MAX;parent = -1;lchild = -1;rchild = -1;codelen = 0;}}HTnode;class huffmanTree{public:huffmanTree();virtual ~huffmanTree();bool count(char *input); //统计各字符出现的次数,将其写入对应结点的权值void create(); //构造huffman树void code(); //计算每个字符的huffman编码void addbit(int bit); //压缩时对一个未满8个bit的byte中加入一个bitvoid resetbyte(); //将byte清空bool compress(char *input, char *output); //压缩函数成功执行返回true 失败falsebool decompress(char *input, char *output); //解压函数成功执行返回true 失败falsevoid compare(char *input, char *output); //将原文件与压缩后的文件比较private:int root; //记录根结点的位置int leafnum; //记录不同字符的个数HTnode HT[leaf*2-1]; //HTnode结构的数组,用来表示huffman树,树的最大结点个数不会超过leaf*2-1char byte; //压缩文件时用来缓冲bit的变量int bitsnum; //byte中bit的个数int lacknum; //压缩到最后byte中的bit不满8个时填充的0的个数};huffmanTree::huffmanTree(){//初始化成员变量root = 0;leafnum = 0;byte = 0;bitsnum = 0;lacknum = 0;}huffmanTree::~huffmanTree(){for(int i=0; i<leaf; i++){if(HT[i].codelen != 0)delete []HT[i].code;}}//统计各字符出现的次数bool huffmanTree::count(char *input){ifstream ifs;char c;ifs.open(input,ios::binary);if(!ifs){cout << "无法打开文件" << input << '!' << endl;return false;}while(ifs.get(c)){if(HT[c+128].weight==MAX){ //若该字符是第一次出现,先初始化权值HT[c+128].weight = 0;leafnum++;}HT[c+128].weight++; //权值+1}ifs.close();return true;}//选权值最小的两棵树组成新的数void huffmanTree::create(){for(int i=leaf; i<2*leaf-1; i++){int loc1=-1, loc2=-1;for(int j=0; j<i; j++){if(HT[j].parent != -1)continue;if(loc1==-1 || HT[j].weight < HT[loc1].weight){loc2 = loc1;loc1 = j;}else if(loc2==-1 || HT[j].weight < HT[loc2].weight)loc2 = j;}if(HT[loc1].weight==MAX || HT[loc2].weight==MAX || loc2==-1) //只剩一棵树,结束break;HT[i].weight = HT[loc1].weight + HT[loc2].weight;//为了减少压缩文件中需要写入的huffman树的信息,约定小标小的结点做为双亲结点的左孩子HT[i].lchild = loc1>loc2 ? loc2 : loc1;HT[i].rchild = loc1>loc2 ? loc1 : loc2;HT[loc1].parent = i; HT[loc2].parent = i;root = i;}}//计算每个字符的huffman编码void huffmanTree::code(){for(int i=0; i<leaf; i++){int len=0;int loc=i;while(HT[loc].parent!=-1){ //计算huffman编码长度len++;loc = HT[loc].parent;}HT[i].codelen = len;HT[i].code = new int[len];loc = i;for(int j=len-1; j>=0; j--){ //从后往前找,记录结点的huffman编码if(loc==HT[HT[loc].parent].lchild)HT[i].code[j] = 0;elseHT[i].code[j] = 1;loc = HT[loc].parent;}}}//压缩时对一个未满8个bit的byte中加入一个bitvoid huffmanTree::addbit(int bit){if(bit == 0)byte = byte << 1; //若新增的bit为0,则直接将byte按位左移elsebyte = ((byte << 1) | 1); //若新增的bit为1,先将byte按位左移,再与1按位或运算bitsnum++;}//将byte清空void huffmanTree::resetbyte(){byte = 0;bitsnum = 0;}//压缩函数成功执行返回true 失败falsebool huffmanTree::compress(char *input, char *output){if( !count(input) )return false;create();code();ifstream ifs;ofstream ofs;ifs.open(input,ios::binary);ofs.open(output,ios::binary);char c;if(!ifs){cout << "无法打开文件" << input << '!' << endl;return false;}if(!ofs){cout << "无法打开文件" << output << '!' << endl;return false;}ofs.put(0); //预留一个字符,等压缩完后在该位置写入不足一个byte的bit个数ofs.put(root-384); //将根节点的位置-384写入(为使该值不超过char的最大表示范围)for(int i=0; i<leaf*2-1; i++){ //写入每个结点的双亲结点位置if(HT[i].parent==-1) //若该节点没有双亲结点,则写入127(一个字节所能表示的最大值)ofs.put(127);else //否则将双亲结点的位置-384再写入(为使该值不超过char的最大表示范围)ofs.put(HT[i].parent-384);}while(ifs.get(c)){ //将字符的huffman编码并加入byte中int tmp = c+128;for(int i=0; i<HT[tmp].codelen; i++){addbit(HT[tmp].code[i]);if(bitsnum==8){ //若byte已满8位,则输出该byte并将byte清空ofs.put(byte);resetbyte();}}}if(bitsnum!=0){ //处理最后未满8个字符的byte,用0填充并记录填充的个数for(int i=bitsnum; i<8; i++){addbit(0);lacknum++;}ofs.put(byte);resetbyte();}ofs.seekp(0,ios::beg); //将写指针移动到文件开头ofs.put(lacknum); //写入最后一个字节缺失的bit个数ifs.close();ofs.close();return true;}//解压函数成功执行返回true 失败falsebool huffmanTree::decompress(char *input, char *output){queue<char> q;char c;ifstream ifs;ofstream ofs;ifs.open(input,ios::binary);ofs.open(output,ios::binary);if(!ifs){cout << "无法打开文件" << input << '!' << endl;return true;}if(!ofs){cout << "无法打开文件" << output << '!' << endl;return false;}ifs.get(c);lacknum = c; //读出最后一个字节缺失的bit个数ifs.get(c);root = c+384; //读出根结点的位置for(int i=0; i<leaf*2-1; i++){ //建立各结点之间的双亲孩子关系ifs.get(c);if(c==127)continue;else{HT[i].parent = c+384;if(HT[c+384].lchild==-1)HT[c+384].lchild = i;elseHT[c+384].rchild = i;}}int point = root;//为了方便处理最后一个可能有缺失bit的字节,先将读出的数据放入队列while(ifs.get(c))q.push(c);//还原文件过程while(q.size()>1){ //还未到最后一个字节c = q.front();for(int i=0; i<8; i++){if(int(c&128)==0){point = HT[point].lchild;if(HT[point].lchild==-1 && HT[point].rchild==-1){ofs.put(char(point-128));point = root;}c = c << 1;}else{point = HT[point].rchild;if(HT[point].lchild==-1 && HT[point].rchild==-1){ofs.put(char(point-128));point = root;}c = c << 1;}}q.pop();}c = q.front(); //最后一个字节for(i=0; i<8-lacknum; i++){if(int(c&128)==0){point = HT[point].lchild;if(HT[point].lchild==-1 && HT[point].rchild==-1){ofs.put(char(point-128));point = root;}c = c << 1;}else{point = HT[point].rchild;if(HT[point].lchild==-1 && HT[point].rchild==-1){ofs.put(char(point-128));point = root;}c = c << 1;}}q.pop();ifs.close();ofs.close();return true;}//将原文件与压缩后的文件比较void huffmanTree::compare(char *input, char *output){ifstream origin, compress;origin.open(input,ios::binary);compress.open(output,ios::binary);if(!origin){cout << "无法打开文件" << input << '!' << endl;return;}if(!compress){cout << "无法打开文件" << output << '!' << endl;return;}double total1=0, total2=0;char c;while(origin.get(c))total1++;while(compress.get(c))total2++;cout << "原文件大小:" << total1 << " Byte" << endl;cout << "压缩后大小:" << total2 << " Byte" << endl;cout << "压缩率:" << total2/total1*100 << '%' << endl;origin.close();compress.close();}void main(){int choice = 1;char input[255], output[255];huffmanTree h;while(choice){cout<<" ****************************************************"<<endl;cout<<" * 基于哈夫曼树的文件压缩/解压程序*"<<endl;cout<<" * *"<<endl;cout<<" * 1) 压缩*"<<endl;cout<<" * *"<<endl;cout<<" * 2) 解压*"<<endl;cout<<" * *"<<endl;cout<<" * 0) 退出*"<<endl;cout<<" * *"<<endl;cout<<" * 说明:请输入相应的操作序号*"<<endl;cout<<" ****************************************************"<<endl;cout<<"请选择:";cin >> choice;switch(choice){case 1:{cout << "请输入待压缩的文件名:";cin >> input;cout << "请输入压缩后的文件名:";cin >> output;if( press(input,output)){pare(input,output);cout<<"文件压缩成功!"<<endl;}else{cout<<"文件压缩失败!"<<endl;}}break;case 2:{cout << "请输入待解压的文件名:";cin >> input;cout << "请输入解压后的文件名:";cin >> output;if (h.decompress(input,output))cout<<"文件解压成功!"<<endl;elsecout<<"文件解压失败!"<<endl;}break;case 0:break;default:cout << "参数错误!请重新输入" << endl;}cout << endl;}}韶关学院计算机科学学院数据结构课程设计题目:基于哈夫曼树的文件压缩/解压程序学生姓名:曹键明学号:11115011018专业:计算机科学与技术班级:11级(1)班指导教师姓名及职称:陈正铭讲师起止时间:2013 年3 月——2013 年4 月1 需求分析1.1课题背景及意义近年来,随着计算机技术的发展,多媒体计算机技术、计算机网络技术以及现代多媒体通信技术正在向着信息化、高速化、智能化迅速发展。
哈夫曼的文件压缩解压程序
调试过程中遇到的问题并且是如何解决的 以及对设计实现的回顾讨论和分析算法的时空 分析(包括基本操作和主要算法的时空复杂度 的分析)和改进设想经验和体会等
12
5、 用户使用说明 向用户说明如何使用你编写的程序,详细列出每
一步的操作步骤。 6、 测试结果
8
STL示例——排序算法
(1)自行写排序算法与调用; (2)调用标准C++函数qsort()完成排序; (3)部分使用STL特性; (4)完全使用STL特性。
9
设计要求
基于哈夫曼树的文件压缩/解压程序
基本要求:实现一个基于哈夫曼树的文件压缩程序和文件解 压程序。要求压缩程序读入源文件,分析每种字符的频度, 然后建立相应的哈夫曼树,再求出相应哈夫曼编码,根据 编码对源文件进行压缩,得到源文件对应的压缩文件。解 压程序读入压缩文件,根据相应的哈夫曼编码解压还原 , 得到对应的源文件。
第2~8周 编写程序,准备测试数据
第9周
上机演示,回答教师提问,按格 式撰写课程设计报告
地点
备注
课室
实验室或 在实验室或宿舍完成编 宿舍 写程序任务
实验室
课程设计报告应严格按 照上述设计报告撰写要 求撰写;最后将报告用 A4纸打印装订;
第10周
以班为单位上交课程设计的资料 光盘(内含每个同学的课程设计 的源程序、编译后可直接运行的 目标程序与课程设计报告的电子 文档),课程设计报告打印稿
2 4
5
79
WPL(T)=
74+94+53+
42+21
=89
18
二、如何构造最优树
哈夫曼编码 压缩
哈夫曼编码压缩标题:哈夫曼编码压缩:一个深度解析一、引言哈夫曼编码是一种用于数据压缩的算法,由戴维·A·哈夫曼在1952年提出。
这种编码方法通过创建一种特殊的二叉树(哈夫曼树)来实现数据压缩。
哈夫曼编码广泛应用于文本文件、音频文件、图像文件等的数据压缩。
二、哈夫曼树的构建哈夫曼树是一种特殊的二叉树,它的特点是左子节点小于父节点,右子节点大于父节点。
哈夫曼树的构建过程如下:1. 初始化:将所有字符及其出现频率作为叶子节点,构成一棵棵只有根节点和一个叶子节点的二叉树。
2. 合并:每次选取两个权值最小的节点,生成一个新的节点,新节点的权值是两个被选取节点权值之和,然后把这两个节点作为新节点的左右孩子。
这样就得到了一颗新的二叉树。
3. 重复第二步,直到只剩下一个节点,这棵树就是我们要找的哈夫曼树。
三、哈夫曼编码哈夫曼编码是指从哈夫曼树的根到每个叶子节点的路径上的0和1的序列。
具体做法是从根节点出发,向左走记为0,向右走记为1。
每个字符的哈夫曼编码就是从根节点到该字符所在叶子节点的路径上的0和1的序列。
四、哈夫曼编码的压缩与解压1. 压缩:对原始数据进行哈夫曼编码,得到压缩后的数据。
2. 解压:对压缩后的数据进行哈夫曼解码,还原出原始数据。
五、哈夫曼编码的优点与缺点优点:1. 数据压缩效率高:哈夫曼编码能够有效地减少数据存储空间,提高数据传输速度。
2. 简单易懂:哈夫曼编码的原理和实现都比较简单,易于理解和实现。
缺点:1. 对于稀疏数据,压缩效果不佳:哈夫曼编码依赖于字符的出现频率,如果字符出现频率相近,压缩效果会降低。
2. 需要额外的存储空间:为了恢复原始数据,需要保存哈夫曼树或者哈夫曼编码表,这会占用一定的存储空间。
六、总结哈夫曼编码是一种有效的数据压缩方法,它通过构建哈夫曼树来实现数据的压缩和解压。
虽然哈夫曼编码有一些缺点,但其高效性和简单性使其在实际应用中得到了广泛的应用。
在未来,随着数据量的不断增大,哈夫曼编码等数据压缩技术将会发挥更大的作用。
哈夫曼树与文件解压压缩C言代码
1.问题描述哈弗曼树的编码与译码—功能:实现对任何类型文件的压缩与解码—输入:源文件,压缩文件—输出:解码正确性判定,统计压缩率、编码与解码速度—要求:使用边编码边统计符号概率的方法〔自适应Huffman编码〕和事先统计概率的方法〔静态Huffman编码〕2.1程序清单程序书签:1.main函数2.压缩函数3.select函数4.encode函数5.解压函数#include <stdio.h>#include <string.h>#include <stdlib.h>#include <conio.h>#include <time.h>struct node{long weight; //权值unsigned char ch;//字符int parent,lchild,rchild;char code[256];//编码的位数最多为256位int CodeLength;//编码长度}hfmnode[512];void compress();void uncompress();//主函数void main(){int choice;printf("请选择1~3:\n");printf("1.压缩文件\n");printf("2.解压文件\n");printf("3.退出!\n");scanf("%d",&choice);if(choice==1)compress();else if(choice==2)uncompress();else if(choice==3)return;else printf("输入错误!");}//压缩函数void compress(){int i,j;char infile[20],outfile[20];FILE *ifp,*ofp;unsigned char c;//long FileLength,filelength=0;int n,m;//叶子数和结点数int s1,s2; //权值最小的两个结点的标号char codes[256];long sumlength=0;float rate,speed;int count=0;clock_t start1, start2,finish1,finish2;double duration1,duration2;void encode(struct node *nodep,int n);//编码函数int select(struct node *nodep,int pose);//用于建哈弗曼树中选择权值最小的结点的函数printf("请输入要压缩的文件名:");scanf("%s",infile);ifp=fopen(infile,"rb");if(ifp==NULL){printf("文件名输入错误,文件不存在!\n");return;}printf("请输入目标文件名:");scanf("%s",outfile);ofp=fopen(outfile,"wb");if(ofp==NULL){printf("文件名输入错误,文件不存在!\n");return;}start1=clock() ;//开始计时1//统计文件中字符的种类以与各类字符的个数//先用字符的ASCII码值代替结点下标FileLength=0;while(!feof(ifp)){fread(&c,1,1,ifp);hfmnode[c].weight++;FileLength++;}FileLength--; //文件中最后一个字符的个数会多统计一次,所以要减一hfmnode[c].weight--;//再将ASCII转换为字符存入到结点的ch成员里,同时给双亲、孩子赋初值-1n=0;for(i=0;i<256;i++)if(hfmnode[i].weight!=0){hfmnode[i].ch=(unsigned char)i;n++;//叶子数hfmnode[i].lchild=hfmnode[i].rchild=hfmnode[i].parent=-1;}m=2*n-1;//哈弗曼树结点总数j=0;for(i=0;i<256;i++)//去掉权值为0的结点if(hfmnode[i].weight!=0){hfmnode[j]=hfmnode[i];j++;}for(i=n;i<m;i++)//初始化根结点{hfmnode[i].lchild=hfmnode[i].rchild=-1;hfmnode[i].parent=-1;}//建立哈弗曼树for(i=n;i<m;i++){s1=select(hfmnode,i-1);hfmnode[i].lchild=s1;hfmnode[s1].parent=i;s2=select(hfmnode,i-1);hfmnode[i].rchild=s2;hfmnode[s2].parent=i;hfmnode[i].weight=hfmnode[s1].weight+hfmnode[s2].weight;}//编码encode(hfmnode,n);finish1=clock();duration1=(double)(finish1- start1) / CLOCKS_PER_SEC;/*printf( "哈弗曼树编码用时为:%f seconds\n", duration1 );*/printf("编码完成,是否查看编码信息: y or n?\n");c=getch();if(c=='y'){ printf("\n");printf("叶子数为%d,结点数为%d\n",n,m);for(i=0;i<n;i++)printf("%d号叶子结点的权值为:%ld,双亲为:%d,左右孩子:%d,编码为:%s\n",i,hfmnode[i].weight,hfmnode[i].parent,hfmnode[i].lchild,hfmnode[i].code);}start2=clock() ;//开始计时2fseek(ifp,0,SEEK_SET);//将ifp指针移到文件开头位置fwrite(&FileLength,4,1,ofp);//将FileLength写入目标文件的前4个字节的位置fseek(ofp,8,SEEK_SET);//再将目标文件指针ofp移到距文件开头8个字节位置codes[0]=0;//将编码信息写入目标文件while(!feof(ifp)){fread(&c,1,1,ifp);filelength++;for(i=0;i<n;i++)if(c==hfmnode[i].ch) break; //ch必须也为unsigned 型strcat(codes,hfmnode[i].code);while(strlen(codes)>=8){for(i=0;i<8;i++)//将codes的前8位01代码表示的字符存入c{if(codes[i]=='1')c=(c<<1)|1;else c=c<<1;}fwrite(&c,1,1,ofp); //将新的字符写入目标文件sumlength++;strcpy(codes,codes+8);//更新codes的值}if(filelength==FileLength) break;}//再将剩余的不足8位的01代码补全8位,继续写入if(strlen(codes)>0){strcat(codes,"00000000");for(i=0;i<8;i++){if(codes[i]=='1')c=(c<<1)|1;else c=c<<1;}fwrite(&c,1,1,ofp);sumlength++;}sumlength+=8;printf("编码区总长为:%ld个字节\n",sumlength-8);//将sumlength和n的值写入目标文件,为的是方便解压fseek(ofp,4,SEEK_SET);fwrite(&sumlength,4,1,ofp);//把sumlength写进目标文件的第5-8个字节里fseek(ofp,sumlength,SEEK_SET);fwrite(&n,4,1,ofp);//把叶子数n写进编码段后面的4个字节的位置//为方便解压,把编码信息存入n后面的位置//存储方式为:n*〔字符值〔1个字节〕+该字符的01编码的位数〔1个字节〕+编码〔字节数不确定,用count来计算总值〕〕for(i=0;i<n;i++){fwrite(&(hfmnode[i].ch),1,1,ofp);c=hfmnode[i].CodeLength;//编码最长为256位,因此只需用一个字节存储fwrite(&c,1,1,ofp);//写入字符的编码if(hfmnode[i].CodeLength%8!=0)for(j=hfmnode[i].CodeLength%8;j<8;j++)//把编码不足8位的在低位补0,赋值给C,再把C写入strcat(hfmnode[i].code,"0");while(hfmnode[i].code[0]!=0)//开始存入编码,每8位二进制数存入一个字节{c=0;for(j=0;j<8;j++){if(hfmnode[i].code[j]=='1')c=(c<<1)|1;else c=c<<1;}strcpy(hfmnode[i].code,hfmnode[i].code+8);//编码前移8位,继续存入编码count++; //编码占的字节数的总值fwrite(&c,1,1,ofp);}}printf("\n");finish2=clock();duration2=(double)(finish2- start2) / CLOCKS_PER_SEC;/*printf( "写入目标文件用时为:%f seconds\n", duration2);*/ printf( "压缩用时为:%f seconds\n", duration1+duration2);speed=(float)FileLength/(duration1+duration2)/1000;printf("\n压缩速率为:%5.2f KB/S\n",speed);printf("\n");printf("源文件长度为:%ld个字节\n",FileLength);sumlength=sumlength+4+n*2+count; //计算压缩后文件的长度printf("压缩后文件长度为:%ld个字节\n",sumlength);rate=(float)sumlength/(float)FileLength;printf("压缩率(百分比)为:%4.2f%%%\n",rate*100);fclose(ifp);fclose(ofp);return;}//返回书签//建立哈弗曼树中用于选择最小权值结点的函数int select(struct node *nodep,int pose){int i;int s1;long min=2147483647;//s初值为long型的最大值for(i=0;i<=pose;i++){if(nodep[i].parent!=-1)continue;if(nodep[i].weight<min){min=nodep[i].weight;s1=i;}}return s1;}//返回书签//哈弗曼编码函数void encode(struct node *nodep,int n){ //从叶子向根求每个字符的哈弗曼编码int start;int i,f,c;char codes[256];codes[n-1]='\0'; //编码结束符for(i=0;i<n;i++) //逐个字符求哈弗曼编码{start=n-1;for(c=i,f=nodep[i].parent;f!=-1;c=f,f=nodep[f].parent){start--;if(nodep[f].lchild==c)codes[start]='0';else codes[start]='1';}strcpy(nodep[i].code,&codes[start]);nodep[i].CodeLength=strlen(nodep[i].code);}}//返回书签//解压函数void uncompress() //解压文件{clock_t start, finish;double duration;FILE *ifp,*ofp;char infile[20],outfile[20];long FileLength,sumlength,filelength;int n,m;int i,j,k;char buf[256],codes[256];unsigned char c;int maxlength;float speed;printf("请输入要解压的文件名:");scanf("%s",infile);ifp=fopen(infile,"rb");if(ifp==NULL){printf("文件名输入错误,文件不存在!\n");return;}printf("请输入目标文件名:");scanf("%s",outfile);ofp=fopen(outfile,"wb");if(ofp==NULL){printf("文件名输入错误,文件不存在!\n");return;}start=clock() ;//开始计时fread(&FileLength,4,1,ifp);//从压缩文件读出FileLength、sumlengthfread(&sumlength,4,1,ifp);fseek(ifp,sumlength,SEEK_SET); //利用sumlength读出n的值fread(&n,4,1,ifp);printf("\n解码信息:源文件长度为%d个字节,字符种类n=%d\n",FileLength,n);for(i=0;i<n;i++)//读结点信息{fread(&hfmnode[i].ch,1,1,ifp);//字符fread(&c,1,1,ifp);//编码长度hfmnode[i].CodeLength=c;hfmnode[i].code[0]=0;if(hfmnode[i].CodeLength%8>0) m=hfmnode[i].CodeLength/8+1;//m为编码占的字节数else m=hfmnode[i].CodeLength/8;for(j=0;j<m;j++)//根据字节长度m读出编码{fread(&c,1,1,ifp);//此处c为01编码转换成的字符itoa(c,buf,2);//字符型编码转换成二进制型〔首位为1〕//如果编码不够8位,则说明缺少了8-k位0,因此应先在前面空缺位写0for(k=8;k>strlen(buf);k--){strcat(hfmnode[i].code,"0");}//再把二进制编码存进hfmnode.code中strcat(hfmnode[i].code,buf);}hfmnode[i].code[hfmnode[i].CodeLength]=0;//去掉编码中多余的0}//找出编码长度的最大值maxlength=0;for(i=0;i<n;i++)if(hfmnode[i].CodeLength>maxlength)maxlength=hfmnode[i].CodeLength;//开始写入目标文件fseek(ifp,8,SEEK_SET); //指针指向编码区,开始解码filelength=0;codes[0]=0;buf[0]=0;while(1){while(strlen(codes)<maxlength)//codes小于编码长度的最大值时,继续读码{fread(&c,1,1,ifp);itoa(c,buf,2);//还原编码for(k=8;k>strlen(buf);k--){strcat(codes,"0");//把缺掉的0补上}strcat(codes,buf);//codes中此时存的为一串01编码}for(i=0;i<n;i++){ //在codes中查找能使其前weight位和hfmnode.code相同的i值,weight 即为codelengthif(memcmp(hfmnode[i].code,codes,(unsignedint)hfmnode[i].CodeLength)==0) break;}strcpy(codes,codes+hfmnode[i].CodeLength);//更新codes的值c=hfmnode[i].ch;fwrite(&c,1,1,ofp);filelength++;if(filelength==FileLength) break;//写入结束}finish = clock();duration = (double)(finish - start) / CLOCKS_PER_SEC;printf( "\n解压完成,解压用时为:%f seconds\n", duration );fseek(ifp,0,SEEK_SET);FileLength=0;while(!feof(ifp)){fread(&c,1,1,ifp);FileLength++;}FileLength--;speed=(float)FileLength/duration/1000;/*printf("此文件长度为:%ld个字节\n",FileLength);*/printf("\n解压速度为:%5.2fKB/S\n",speed);fclose(ifp);fclose(ofp);return;}2.2程序运行结果:1.对文件〞测试.txt〞进行压缩,压缩后存储在文件〞目标.doc〞中,压缩速率为:2055.00KB/S,压缩率为64.92%。
哈夫曼编码的压缩与解压缩
哈夫曼编码的压缩与解压缩1.引言1.1 概述哈夫曼编码是一种常用的数据压缩算法,它采用了一种变长编码方式,将出现频率高的字符用较短的编码表示,出现频率低的字符用较长的编码表示,以达到压缩数据的目的。
该编码方法由美国数学家大卫·哈夫曼于1952年提出,被广泛应用于各种数据压缩和传输领域。
在传统的固定长度编码中,每个字符都使用相同的位数来表示,因此在表示不同概率出现的字符时,可能会浪费大量的位数。
而哈夫曼编码则是根据字符在文本中出现的频率来确定其对应的编码,使得高频出现的字符用更短的编码表示,低频出现的字符用较长的编码表示。
哈夫曼编码的核心思想是构建一棵哈夫曼树,将出现频率作为权值,频率越高的字符离根节点越近。
通过从叶子节点到根节点的路径确定每个字符的编码,即将左子树标记为0,右子树标记为1。
在对文本进行压缩时,将文本中的字符转换为其对应的哈夫曼编码,即可将原始数据压缩为较短的二进制串。
相比于传统固定长度编码,哈夫曼编码具有显著的优势。
首先,它可以根据文本中字符出现的实际情况进行编码,使得频率高的字符用较短的编码表示,从而大幅度减少了编码后的数据长度。
其次,哈夫曼编码是一种前缀编码,即任何一个字符的编码都不是其他字符编码的前缀,这样在解码时可以直接根据编码找到对应的字符,无需回溯查表,提高了解码效率。
哈夫曼编码的应用广泛,它被用于无损图像压缩、音频压缩、文本压缩等领域。
随着互联网和大数据时代的到来,数据的传输和存储成为重要的问题,如何高效地压缩和解压缩数据成为一个热门的研究方向。
哈夫曼编码作为一种简单而有效的压缩算法,具有广阔的应用前景。
然而,哈夫曼编码也存在一些问题,如编码时需要构建哈夫曼树,需要额外的空间和时间开销,对于小规模数据可能会影响压缩效率。
因此,在实际应用中,需要综合考虑数据规模和应用场景,选择合适的压缩算法。
1.2 文章结构本文主要介绍了哈夫曼编码的压缩与解压缩。
文章分为引言、正文和结论三个部分。
哈夫曼的文件压缩解压程序课件
对数据进行预处理,例如去除冗余信息、进行数据格式 化等,以减少需要压缩的数据量。
使用更高效的编码和解码算法,例如使用位操作代替字 符串操作,减少内存占用和计算时间。
采用动态哈夫曼编码技术,根据数据流的特点动态调整 哈夫曼树的结构,以实现更好的压缩效果。
文件压缩原理
文件压缩的概念
01
02
03
文件压缩
通过特定的算法对文件进 行编码,以减少其存储空 间的需求,同时保持文件 完整性和可读性。
压缩文件
经过压缩处理后的文件, 其大小明显小于原始文件 。
解压缩
将压缩文件还原为原始大 小的过程。
文件压缩的原理
数据冗余
大多数文件都包含大量的 数据冗余,如重复的模式 或数据块。
编码
利用数据冗余,通过特定 的算法对数据进行编码, 以减少存储空间需求。
熵编码
根据数据的概率分布进行 编码,使常见数据使用较 短的编码,罕见数据使用 较长的编码。
常见文件压缩算法
Huffman编码
Bzip2
一种基于熵编码的算法,根据数据的 概率分布创建字符到二进制码的映射 。
使用Burrows-Wheeler变换和 Huffman编码的压缩算法,具有较高 的压缩比和较好的数据完整性。
因素。
06
哈夫曼压缩解压程序应用 实例
哈夫曼压缩解压程序的使用场景
1 2
数据存储
哈夫曼压缩解压程序常用于数据存储领域,能够 有效地减小数据文件的大小,节省存储空间。
网络传输
在网络传输中,哈夫曼压缩解压程序能够降低传 输时间和带宽消耗,提高传输效率。
3
实时处理
哈夫曼算法进行文件压缩和解压缩
本文首先简要阐述哈夫曼算法的基本思想,然后介绍了使用哈夫曼算法进行文件压缩和解压缩的处理步骤,最后给出了C语言实现的文件压缩和解压缩的源代码。
哈夫曼算法的主要思想是:①首先遍历要处理的字符串,得到每个字符的出现的次数;②将每个字符(以其出现次数为权值)分别构造为二叉树(注意此时的二叉树只有一个节点);③取所有二叉树种种字符出现次数最小的二叉树合并为一颗新的二叉树,新二叉树根节点的权值等于两个子节点的权值之和,新节点中的字符忽略;④重复过程③直到所有树被合并为同一棵二叉树⑤遍历最后得到的二叉树,自顶向下按路径编号,指向左节点的边编号0,指向右节点的边编号1,从根到叶节点的所有边上的0和1链接起来,就是叶子节点中字符的哈夫曼编码。
下图展示了哈夫曼编码的基本思想。
基于哈夫曼算法的文件压缩和解压缩过程分别说明如下:一、文件压缩:①统计词频:读取文件的每个字节,使用整数数组int statistic[MAX_CHARS]统计每个字符出现的次数,由于一个字节最多表示2^8-1个字符,所以MAX_CHARS=256就足够了。
在统计字符数的时候,对于每一个byte, 有statistic[(unsigned char)byte]++。
②构造哈夫曼树:根据statistic数组,基于哈夫曼树算法造哈夫曼树,由于构造的过程中每次都要取最小权值的字符,所以需要用优先队列来维护每棵树的根节点。
③生成编码:深度优先遍历哈弗曼树,得到每个叶子节点中的字符的编码并存入字符串数组char *dictionary[MAX_CHARS];④存储词频:新建存储压缩数据的文件,首先写入不同字符的个数,然后将每个字符及其对应的词频写入文件。
⑤存储压缩数据:再次读取待压缩文件的每个字节byte,由dictionary[(unsigned int)byte]得到对应的编码(注意每个字符编码的长度不一),使用位运算一次将编码中的每个位(BIT)设置到一个char 类型的位缓冲中,可能多个编码才能填满一个位缓冲,每填满一次,将位缓冲区以单个字节的形式写入文件。
java实现哈弗曼树和哈夫曼树压缩
java实现哈弗曼树和哈夫曼树压缩本篇博⽂将介绍什么是哈夫曼树,并且如何在java语⾔中构建⼀棵哈夫曼树,怎么利⽤哈夫曼树实现对⽂件的压缩和解压。
⾸先,先来了解下什么哈夫曼树。
⼀、哈夫曼树哈夫曼树属于⼆叉树,即树的结点最多拥有2个孩⼦结点。
若该⼆叉树带权路径长度达到最⼩,称这样的⼆叉树为最优⼆叉树,也称为哈夫曼树(Huffman Tree)。
哈夫曼树是带权路径长度最短的树,权值较⼤的结点离根较近。
(⼀)树的相关概念1.路径和路径长度在⼀棵树中,从⼀个结点往下可以达到的孩⼦或孙⼦结点之间的通路,称为路径。
通路中分⽀的数⽬称为路径长度。
若规定根结点的层数为1,则从跟结点到第L层结点的路径长度为L-1。
2.结点的权和带权路径长度若将树中结点赋给⼀个有着某种含义的数值,则这个数值称为该结点的权。
结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
3.树的带权路径长度树的带权路径长度规定为所有叶⼦结点的带权路径长度之和,记为WPL。
(⼆)哈夫曼树的构造原理假设有n个权值,则构造出的哈夫曼树有n个叶⼦结点。
n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有⼀个结点);(2) 在森林中选出两个根结点的权值最⼩的树合并,作为⼀棵新树的左、右⼦树,且新树的根结点权值为其左、右⼦树根结点权值之和;(3)从森林中删除选取的两棵树,并将新树加⼊森林;(4)重复(2)、(3)步,直到森林中只剩⼀棵树为⽌,该树即为所求得的哈夫曼树。
(三)哈夫曼编码在数据通信中,需要将传送的⽂字转换成⼆进制的字符串,⽤0,1码的不同排列来表⽰字符。
例如,需传送的报⽂为“AFTER DATA EAR ARE ART AREA”,这⾥⽤到的字符集为“A,E,R,T,F,D”,各字母出现的次数为{8,4,5,3,1,1}。
现要求为这些字母设计编码。
哈夫曼编码使用哈夫曼编码进行数据压缩与解压缩
哈夫曼编码使用哈夫曼编码进行数据压缩与解压缩在信息技术发展的背景下,数据的传输和存储需求越来越大,如何高效地进行数据压缩成为了重要的课题之一。
哈夫曼编码是一种基于信息熵的编码技术,通过构建最优二叉树,实现了数据的高效压缩和解压缩。
在本文中,我们将探讨哈夫曼编码的原理和应用。
一、哈夫曼编码的原理哈夫曼编码是一种变长编码,它根据字符出现的频率构建一棵最优二叉树,频率较高的字符被赋予较短的编码,频率较低的字符则被赋予较长的编码。
这种编码方式具有无重复前缀码性质,即任意字符的编码都不是其他字符编码的前缀。
这样一来,在解码时就能够准确还原原始数据。
具体实现过程如下:1. 统计字符频率:对待编码的数据进行字符频率统计,得到每个字符出现的次数。
2. 构建哈夫曼树:根据字符频率构建一棵哈夫曼树。
树的每个节点表示一个字符,并且字符的频率决定节点的权重。
构建过程中,每次选择权重最小的两个节点合并形成新的节点,直到所有节点合并为一棵树。
3. 分配编码:从根节点出发,对哈夫曼树的每个子树进行遍历,左子树路径上追加0,右子树路径上追加1,直到到达叶节点。
这样,每个字符都能够找到对应的编码。
4. 进行数据压缩:根据字符的编码,将原始数据进行编码替换,形成压缩后的数据。
编码后的数据长度会变短,达到了数据压缩的目的。
二、哈夫曼编码的应用哈夫曼编码在数据压缩领域有着广泛的应用。
其中,最常见的应用场景是在无损压缩算法中。
通过哈夫曼编码,能够将原始数据进行高效压缩,并在解压缩时准确还原数据,但并不损失任何信息。
此外,哈夫曼编码还经常用于文件压缩、图像压缩和音频压缩等领域。
在文件压缩中,能够将大文件转换为更小的压缩文件,方便传输和存储。
在图像压缩中,通过对图像数据进行编码,能够减小图像文件的大小,提高传输效率。
在音频压缩中,通过压缩音频数据,减小文件大小,同时保持音频质量,实现高质量的音频传输。
三、哈夫曼编码的优缺点1. 优点:哈夫曼编码是一种高效的数据压缩技术,可以大幅度减小数据的存储和传输空间。
用哈夫曼树算法写对文件压缩与解压缩代码
用哈夫曼树算法设计对文件文件的压缩和解压缩的程序怎么写?基本要求:(1)文件名的输入可以从命令行给出或程序界面给出;(2)压缩和解压选择也可以从命令行给出或程序界面给出;(3)给出压缩后的指标:压缩率=压缩后的文件大小/压缩前的文件大小最佳答案#include <stdio.h>#include <string.h>#include <stdlib.h>#include <conio.h>struct head{unsigned char b; /*the charactor*/long count; /*the frequency*/long parent,lch,rch; /*make a tree*/char bits[256]; /*the haffuman code*/}header[512],tmp;void compress(){char filename[255],outputfile[255],buf[512];unsigned char c;long i,j,m,n,f;long min1,pt1,flength;FILE *ifp,*ofp;printf("source filename:");gets(filename);ifp=fopen(filename,"rb");if(ifp==NULL){printf("source file open error!\n");return;}printf("destination filename:");gets(outputfile);ofp=fopen(outputfile,"wb");if(ofp==NULL){printf("destination file open error!\n");return;}flength=0;while(!feof(ifp)){fread(&c,1,1,ifp);header[c].count++;flength++;}flength--;header[c].count--;for(i=0;i<512;i++){if(header[i].count!=0) header[i].b=(unsigned char)i; else header[i].b=0;header[i].parent=-1;header[i].lch=header[i].rch=-1;}for(i=0;i<256;i++){for(j=i+1;j<256;j++){if(header[i].count<header[j].count){tmp=header[i];header[i]=header[j];header[j]=tmp;}}}for(i=0;i<256;i++) if(header[i].count==0) break;n=i;m=2*n-1;for(i=n;i<m;i++){min1=999999999;for(j=0;j<i;j++){if(header[j].parent!=-1) continue;if(min1>header[j].count){pt1=j;min1=header[j].count;continue;}}header[i].count=header[pt1].count;header[pt1].parent=i;header[i].lch=pt1;min1=999999999;for(j=0;j<i;j++){if(header[j].parent!=-1) continue;if(min1>header[j].count){pt1=j;min1=header[j].count;continue;}}header[i].count+=header[pt1].count;header[i].rch=pt1;header[pt1].parent=i;}for(i=0;i<n;i++){f=i;header[i].bits[0]=0;while(header[f].parent!=-1){j=f;f=header[f].parent;if(header[f].lch==j){j=strlen(header[i].bits);memmove(header[i].bits+1,header[i].bits,j+1);header[i].bits[0]='0';}else\ {j=strlen(header[i].bits);memmove(header[i].bits+1,header[i].bits,j+1);header[i].bits[0]='1';}}}fseek(ifp,0,SEEK_SET);fwrite(&flength,sizeof(int),1,ofp);fseek(ofp,8,SEEK_SET);buf[0]=0;f=0;pt1=8;while(!feof(ifp)){c=fgetc(ifp);f++;for(i=0;i<n;i++){if(c==header[i].b) break;}strcat(buf,header[i].bits);j=strlen(buf);c=0;while(j>=8){for(i=0;i<8;i++){if(buf[i]=='1') c=(c<<1)|1;else c=c<<1;}fwrite(&c,1,1,ofp);pt1++;strcpy(buf,buf+8);j=strlen(buf);}if(f==flength) break;}if(j>0){strcat(buf,"00000000");for(i=0;i<8;i++){if(buf[i]=='1') c=(c<<1)|1;else c=c<<1;}fwrite(&c,1,1,ofp);pt1++;}fseek(ofp,4,SEEK_SET);fwrite(&pt1,sizeof(long),1,ofp); fseek(ofp,pt1,SEEK_SET);fwrite(&n,sizeof(long),1,ofp);for(i=0;i<n;i++){fwrite(&(header[i].b),1,1,ofp);c=strlen(header[i].bits);fwrite(&c,1,1,ofp);j=strlen(header[i].bits);if(j%8!=0){for(f=j%8;f<8;f++)strcat(header[i].bits,"0");}while(header[i].bits[0]!=0){c=0;for(j=0;j<8;j++){if(header[i].bits[j]=='1') c=(c<<1)|1;else c=c<<1;}strcpy(header[i].bits,header[i].bits+8);fwrite(&c,1,1,ofp);}}fclose(ifp);fclose(ofp);printf("compress successfully!\n");return;}void uncompress(){char filename[255],outputfile[255],buf[255],bx[255]; unsigned char c;long i,j,m,n,f,p,l;long flength;FILE *ifp,*ofp;printf("source filename:");gets(filename);ifp=fopen(filename,"rb");if(ifp==NULL){printf("source file open error!\n");return;}printf("destination filename:");gets(outputfile);ofp=fopen(outputfile,"wb");if(ofp==NULL){printf("destination file open error!\n"); return;}fread(&flength,sizeof(long),1,ifp);fread(&f,sizeof(long),1,ifp);fseek(ifp,f,SEEK_SET);fread(&n,sizeof(long),1,ifp);for(i=0;i<n;i++){fread(&header[i].b,1,1,ifp);fread(&c,1,1,ifp);p=(long)c;header[i].count=p;header[i].bits[0]=0;if(p%8>0) m=p/8+1;else m=p/8;for(j=0;j<m;j++){fread(&c,1,1,ifp);f=c;itoa(f,buf,2);f=strlen(buf);for(l=8;l>f;l--){strcat(header[i].bits,"0");}strcat(header[i].bits,buf);}header[i].bits[p]=0;}for(i=0;i<n;i++){for(j=i+1;j<n;j++){if(strlen(header[i].bits)>strlen(header[j].bits)) {tmp=header[i];header[i]=header[j];header[j]=tmp;}}}p=strlen(header[n-1].bits);fseek(ifp,8,SEEK_SET);m=0;bx[0]=0;while(1){while(strlen(bx)<(unsigned int)p){fread(&c,1,1,ifp);f=c;itoa(f,buf,2);f=strlen(buf);for(l=8;l>f;l--){strcat(bx,"0");}strcat(bx,buf);}for(i=0;i<n;i++){if(memcmp(header[i].bits,bx,header[i].count)==0) break; }strcpy(bx,bx+header[i].count);c=header[i].b;fwrite(&c,1,1,ofp);m++;if(m==flength) break;}fclose(ifp);fclose(ofp);printf("Uncompress successfully!\n");return;}int main(){int c;printf("1--Compress file\n");printf("2--Uncompress file\n");printf("Select 1 or 2:");c=getch();printf("%c\n",c);if(c=='1') compress();else if(c=='2') uncompress();return 0;}。
哈夫曼树与文件解压压缩C言代码
哈夫曼树与文件解压压缩C言代码1.问题描述哈弗曼树的编码与译码—功能:实现对任何类型文件的压缩与解码—输入:源文件,压缩文件—输出:解码正确性判定,统计压缩率、编码与解码速度—要求:使用边编码边统计符号概率的方法(自适应Huffman编码)和事先统计概率的方法(静态Huffman编码)2.1程序清单程序书签:1.main函数2.压缩函数3.select函数4.encode函数5.解压函数#include <stdio.h>#include <string.h>#include <stdlib.h>#include <conio.h>#include <time.h>struct node{long weight; //权值unsigned char ch;//字符int parent,lchild,rchild;char code[256];//编码的位数最多为256位int CodeLength;//编码长度}hfmnode[512];void compress();void uncompress();//主函数void main(){int choice;printf("请选择1~3:\n");printf("1.压缩文件\n");printf("2.解压文件\n");printf("3.退出!\n");scanf("%d",&choice);if(choice==1)compress();else if(choice==2)uncompress();else if(choice==3)return;else printf("输入错误!");}//压缩函数void compress(){int i,j;char infile[20],outfile[20];FILE *ifp,*ofp;unsigned char c;//long FileLength,filelength=0;int n,m;//叶子数和结点数int s1,s2; //权值最小的两个结点的标号char codes[256];long sumlength=0;float rate,speed;int count=0;clock_t start1, start2,finish1,finish2;double duration1,duration2;void encode(struct node *nodep,int n);//编码函数int select(struct node *nodep,int pose);//用于建哈弗曼树中选择权值最小的结点的函数printf("请输入要压缩的文件名:");scanf("%s",infile);ifp=fopen(infile,"rb");if(ifp==NULL){printf("文件名输入错误,文件不存在!\n");return;}printf("请输入目标文件名:");scanf("%s",outfile);ofp=fopen(outfile,"wb");if(ofp==NULL){printf("文件名输入错误,文件不存在!\n");return;}start1=clock() ;//开始计时1//统计文件中字符的种类以及各类字符的个数//先用字符的ASCII码值代替结点下标FileLength=0;while(!feof(ifp)){fread(&c,1,1,ifp);hfmnode[c].weight++;FileLength++;}FileLength--; //文件中最后一个字符的个数会多统计一次,所以要减一hfmnode[c].weight--;//再将ASCII转换为字符存入到结点的ch成员里,同时给双亲、孩子赋初值-1n=0;for(i=0;i<256;i++)if(hfmnode[i].weight!=0){hfmnode[i].ch=(unsigned char)i;n++;//叶子数hfmnode[i].lchild=hfmnode[i].rchild=hfmnode[i].parent=-1;}m=2*n-1;//哈弗曼树结点总数j=0;for(i=0;i<256;i++)//去掉权值为0的结点if(hfmnode[i].weight!=0){hfmnode[j]=hfmnode[i];j++;}for(i=n;i<m;i++)//初始化根结点{hfmnode[i].lchild=hfmnode[i].rchild=-1;hfmnode[i].parent=-1;}//建立哈弗曼树for(i=n;i<m;i++){s1=select(hfmnode,i-1);hfmnode[i].lchild=s1;hfmnode[s1].parent=i;s2=select(hfmnode,i-1);hfmnode[i].rchild=s2;hfmnode[s2].parent=i;hfmnode[i].weight=hfmnode[s1].weight+hfmnode[s2].weight;}//编码encode(hfmnode,n);finish1=clock();duration1=(double)(finish1- start1) / CLOCKS_PER_SEC;/*printf( "哈弗曼树编码用时为:%f seconds\n", duration1 );*/printf("编码完成,是否查看编码信息: y or n?\n");c=getch();if(c=='y'){ printf("\n");printf("叶子数为%d,结点数为%d\n",n,m);for(i=0;i<n;i++)printf("%d号叶子结点的权值为:%ld,双亲为:%d,左右孩子:%d,编码为:%s\n",i,hfmnode[i].weight,hfmnode[i].parent,hfmnode[i].lchild,hfmnode[i].code);}start2=clock() ;//开始计时2fseek(ifp,0,SEEK_SET);//将ifp指针移到文件开头位置fwrite(&FileLength,4,1,ofp);//将FileLength写入目标文件的前4个字节的位置fseek(ofp,8,SEEK_SET);//再将目标文件指针ofp移到距文件开头8个字节位置codes[0]=0;//将编码信息写入目标文件while(!feof(ifp)){fread(&c,1,1,ifp);filelength++;for(i=0;i<n;i++)if(c==hfmnode[i].ch) break; //ch必须也为unsigned 型strcat(codes,hfmnode[i].code);while(strlen(codes)>=8){for(i=0;i<8;i++)//将codes的前8位01代码表示的字符存入c{if(codes[i]=='1')c=(c<<1)|1;else c=c<<1;}fwrite(&c,1,1,ofp); //将新的字符写入目标文件sumlength++;strcpy(codes,codes+8);//更新codes的值}if(filelength==FileLength) break;}//再将剩余的不足8位的01代码补全8位,继续写入if(strlen(codes)>0){strcat(codes,"00000000");for(i=0;i<8;i++){if(codes[i]=='1')c=(c<<1)|1;else c=c<<1;}fwrite(&c,1,1,ofp);sumlength++;}sumlength+=8;printf("编码区总长为:%ld个字节\n",sumlength-8);//将sumlength和n的值写入目标文件,为的是方便解压fseek(ofp,4,SEEK_SET);fwrite(&sumlength,4,1,ofp);//把sumlength写进目标文件的第5-8个字节里fseek(ofp,sumlength,SEEK_SET);fwrite(&n,4,1,ofp);//把叶子数n写进编码段后面的4个字节的位置//为方便解压,把编码信息存入n后面的位置//存储方式为:n*(字符值(1个字节)+该字符的01编码的位数(1个字节)+编码(字节数不确定,用count来计算总值))for(i=0;i<n;i++){fwrite(&(hfmnode[i].ch),1,1,ofp);c=hfmnode[i].CodeLength;//编码最长为256位,因此只需用一个字节存储fwrite(&c,1,1,ofp);//写入字符的编码if(hfmnode[i].CodeLength%8!=0)for(j=hfmnode[i].CodeLength%8;j<8;j++)//把编码不足8位的在低位补0,赋值给C,再把C写入strcat(hfmnode[i].code,"0");while(hfmnode[i].code[0]!=0)//开始存入编码,每8位二进制数存入一个字节{c=0;for(j=0;j<8;j++){if(hfmnode[i].code[j]=='1')c=(c<<1)|1;else c=c<<1;}strcpy(hfmnode[i].code,hfmnode[i].code+8);//编码前移8位,继续存入编码count++; //编码占的字节数的总值fwrite(&c,1,1,ofp);}}printf("\n");finish2=clock();duration2=(double)(finish2- start2) / CLOCKS_PER_SEC;/*printf( "写入目标文件用时为:%f seconds\n", duration2);*/printf( "压缩用时为:%f seconds\n", duration1+duration2);speed=(float)FileLength/(duration1+duration2)/1000;printf("\n压缩速率为:%5.2f KB/S\n",speed);printf("\n");printf("源文件长度为:%ld个字节\n",FileLength);sumlength=sumlength+4+n*2+count; //计算压缩后文件的长度printf("压缩后文件长度为:%ld个字节\n",sumlength);rate=(float)sumlength/(float)FileLength;printf("压缩率(百分比)为:%4.2f%%%\n",rate*100);fclose(ifp);fclose(ofp);return;}//返回书签//建立哈弗曼树中用于选择最小权值结点的函数int select(struct node *nodep,int pose){int i;int s1;long min=2147483647;//s初值为long型的最大值for(i=0;i<=pose;i++){if(nodep[i].parent!=-1)continue;if(nodep[i].weight<min){min=nodep[i].weight;s1=i;}}return s1;}//返回书签//哈弗曼编码函数void encode(struct node *nodep,int n){ //从叶子向根求每个字符的哈弗曼编码int start;int i,f,c;char codes[256];codes[n-1]='\0'; //编码结束符for(i=0;i<n;i++) //逐个字符求哈弗曼编码{start=n-1;for(c=i,f=nodep[i].parent;f!=-1;c=f,f=nodep[f].parent){start--;if(nodep[f].lchild==c)codes[start]='0';else codes[start]='1';}strcpy(nodep[i].code,&codes[start]);nodep[i].CodeLength=strlen(nodep[i].code);}}//返回书签//解压函数void uncompress() //解压文件{clock_t start, finish;double duration;FILE *ifp,*ofp;char infile[20],outfile[20];long FileLength,sumlength,filelength;int n,m;int i,j,k;char buf[256],codes[256];unsigned char c;int maxlength;float speed;printf("请输入要解压的文件名:");scanf("%s",infile);ifp=fopen(infile,"rb");if(ifp==NULL){printf("文件名输入错误,文件不存在!\n");return;}printf("请输入目标文件名:");scanf("%s",outfile);ofp=fopen(outfile,"wb");if(ofp==NULL){printf("文件名输入错误,文件不存在!\n");return;}start=clock() ;//开始计时fread(&FileLength,4,1,ifp);//从压缩文件读出FileLength、sumlengthfread(&sumlength,4,1,ifp);fseek(ifp,sumlength,SEEK_SET); //利用sumlength读出n的值fread(&n,4,1,ifp);printf("\n解码信息:源文件长度为%d个字节,字符种类n=%d\n",FileLength,n);for(i=0;i<n;i++)//读结点信息{fread(&hfmnode[i].ch,1,1,ifp);//字符fread(&c,1,1,ifp);//编码长度hfmnode[i].CodeLength=c;hfmnode[i].code[0]=0;if(hfmnode[i].CodeLength%8>0) m=hfmnode[i].CodeLength/8+1;//m为编码占的字节数else m=hfmnode[i].CodeLength/8;for(j=0;j<m;j++)//根据字节长度m读出编码{fread(&c,1,1,ifp);//此处c为01编码转换成的字符itoa(c,buf,2);//字符型编码转换成二进制型(首位为1)//如果编码不够8位,则说明缺少了8-k位0,因此应先在前面空缺位写0for(k=8;k>strlen(buf);k--){strcat(hfmnode[i].code,"0");}//再把二进制编码存进hfmnode.code中strcat(hfmnode[i].code,buf);}hfmnode[i].code[hfmnode[i].CodeLength]=0;//去掉编码中多余的0 }//找出编码长度的最大值maxlength=0;for(i=0;i<n;i++)if(hfmnode[i].CodeLength>maxlength)maxlength=hfmnode[i].CodeLength;//开始写入目标文件fseek(ifp,8,SEEK_SET); //指针指向编码区,开始解码filelength=0;codes[0]=0;buf[0]=0;while(1){while(strlen(codes)<maxlength)//codes小于编码长度的最大值时,继续读码{fread(&c,1,1,ifp);itoa(c,buf,2);//还原编码for(k=8;k>strlen(buf);k--){strcat(codes,"0");//把缺掉的0补上}strcat(codes,buf);//codes中此时存的为一串01编码}for(i=0;i<n;i++){ //在codes中查找能使其前weight位和hfmnode.code相同的i值,weight 即为codelengthif(memcmp(hfmnode[i].code,codes,(unsignedint)hfmnode[i].CodeLength)==0) break;}strcpy(codes,codes+hfmnode[i].CodeLength);//更新codes的值c=hfmnode[i].ch;fwrite(&c,1,1,ofp);filelength++;if(filelength==FileLength) break;//写入结束}finish = clock();duration = (double)(finish - start) / CLOCKS_PER_SEC;printf( "\n解压完成,解压用时为:%f seconds\n", duration );fseek(ifp,0,SEEK_SET);FileLength=0;while(!feof(ifp)){fread(&c,1,1,ifp);FileLength++;}FileLength--;speed=(float)FileLength/duration/1000;/*printf("此文件长度为:%ld个字节\n",FileLength);*/printf("\n解压速度为:%5.2fKB/S\n",speed);fclose(ifp);fclose(ofp);return;}2.2程序运行结果:1.对文件”测试.txt”进行压缩,压缩后存储在文件”目标.doc”中,压缩速率为:2055.00KB/S,压缩率为64.92%。
基于哈夫曼树的文件压缩解压程序-示例文档
软件课程设计报告基于哈夫曼树的文件压缩/解压程序计算机科学学院××专业××班××号×××2009-10-20一需求分析1.课题要求(实现文件的压缩与解压并计算压缩率)A.描述压缩基本符号的选择方法B.运行时压缩原文件的规模应不小于5KC.提供恢复文件与原文件相同性对比功能2.设计目标A软件名称:基于哈夫曼编码的文件压缩实用程序系统B软件组成:Winhfm.exe dosHfm.exeC制作平台及相关调试工具:Windows2000sp4 Borland C++ Builder 6Dev-C++ 4.9.6.0 UltraEdit-32D运行环境:dos/win98/winMe/win2K/winxpE性能特点:1.软件由两个可执行文件组成,各具特点DosHfm.exe为dos系统应用程序,体积小,高效快捷,适用范围广。
WinHfm.exe 为windows应用程序,界面友好,使用方便。
2. 对单字节(256叶子)进行哈夫曼编码,压缩率良好2.使用二级缓冲压缩/解压技术,速度比一般算法高75%以上3.可压缩最大体积为4G的文件,达到Fat32文件系统极限4. 文件索引体积比常规算法小50%5.压缩过程中显示压缩进度并有相关信息提示6.WinHfm.exe可图形显示源文件的哈夫曼编码构造树二概要设计1.相关函数介绍dos版本程序下的有关函数1..void Haffman(int nodeCode,int length,int sum,vector< pair<int,int> >&hfmCode,vector<int> &lchild,vector<int> &rchild)哈夫曼树编码递归函数2.void indexSearch(int nodeCode,vector<int> &lchild,vector<int> &rchild,vector<int> &index,vector<int>&code)索引编码递归函数3.void makeIndex(int nodeCode,int &tt,vector<int> &index,int &indexNum,list<int> &code,vector<int> &lchild,vector<int> &rchild)索引解码递归函数4.void Compress() 压缩函数5.void UnCompress() 解压缩函数windows版本程序下的新增函数1.AnsiString ShowNowTime()计算当前时间(精确到毫秒,以字符串返回)。
利用改进的哈夫曼编码实现文件的压缩与解压
L u Bi n g , L i u Xi n g h a i
( Z h e n g z h o u I n s t i t u t e o f L i g h t I n d u s t r y , C o mp u t e r a n d C o m mu n i c a t i o n E n g i n e e i r n g I n s t i t u t e , Z h e n g z h o u 4 5 0 0 0 2 , C h i n a )
p o s e d . Ac c o r d i n g t o t h e c l a s s i c Hu f ma n a l g o it r h m, u s i n g t h e h e a p s o r t t h o u g h t t o b u i l d t h e Hu f ma n t r e e a n d t h e Hu f - ma n c o d i n g ,t h i s me t h o d c a n r e d u c e t h e me mo r y r e a d a n d wr i t e t i me s , i mp r o v i n g t h e s y s t e m r e s p o n s e t i me . T h r o u g h t h e s e c o n d ma p p i n g , e a c h 8 e n c o d e d il f e b i n a y r i s c o n v e te r d i n t o a c o r r e s p o n d i n g c h a r a c t e r , i mp r o v e t h e c o mp r e s s i o n r a t i o o f i f l e s a n d e n s u r e t h e s e c u i r t y a n d c o n i f d e n t i a l i t y o f t h e r e s u l t i n g c o mp r e s s e d i f l e . F i n a l l y , t h r e e t e x t f i l e s c o mp r e s s i o n t e s t o n he t i mp r o v e d Hu f f ma n a l g o r i t h m, e x p e i r me n t s s h o w t h a t t h e i mp r o v e d a l g o r i t h m, t h e c o mp r e s s i o n r a t i o i s s l i g h t l y b e t —
java实现哈夫曼压缩与解压缩的方法
java实现哈夫曼压缩与解压缩的⽅法⼀哈夫曼树以及⽂件压缩原理:1.哈夫曼树:给定N个权值作为N个叶⼦结点,构造⼀棵⼆叉树,若该树的带权路径长度达到最⼩,称这样的⼆叉树为最优⼆叉树,也称为哈夫曼树。
哈夫曼树是带权路径长度最短的树,权值较⼤的结点离根较近(频率越⾼的结点离根越进)。
以下数组为例,构建哈夫曼树int a[] = {0,1,2,3,4,5,6,7,8}我们可以发现以下规律1:9个数构成的哈夫曼树⼀共有17个结点,也就是可以n个数可以⽣产2*n-1个结点2:数字越⼤的数离根节点越近,越⼩的数离根节点越近。
2.如何利⽤haffman编码实现⽂件压缩:⽐如abc.txt⽂件中有以下字符:aaaabbbccde,1.进⾏字符统计aaaabbbccdea : 4次b : 3次c : 2次d : 1次e : 1次2.⽤统计结果构建哈夫曼树3.⽤哈夫曼树⽣成哈夫曼编码(从根结点开始,路径左边记为0,右边记为1):a的编码:1b的编码:01c的编码:000d的编码:0011e的编码:00104.哈夫曼编码代替字符,进⾏压缩。
源⽂件内容为:aaaabbbccde将源⽂件⽤对应的哈夫曼编码(haffman code)替换,则有:11110101 01000000 00110010 (总共3个字节)由此可见,源⽂件⼀共有11个字符,占11字节的内存,但是经过⽤haffman code替换之后,只占3个字节,这样就能达到压缩的⽬的⼆主要技术点:1.哈夫曼树算法(哈夫曼压缩的基本算法)2.哈希算法(字符统计时候会⽤到,也可以直接⽤HashMap统计)3.位运算(涉及到将指定位,置0或置1)4.java⽂件操作,以及缓冲操作。
5.存储模式(⼤端存储,⼩端存储,能看懂⽂件16进制的形式)7.设置压缩密码,解压输⼊密码解压(⼩编⾃⼰加的内容)三实现过程:以上述aaaabbbccde为例1.字符统计:public class FreqHuf {public static int BUFFER_SIZE = 1 << 18;int freq[] = new int[256];File file;int count;List<HuffmanFreq> list;FreqHuf(String pathname) throws Exception {list = new ArrayList<>();this.file = new File(pathname);if(!file.exists()){throw new Exception("⽂件不存在");}System.out.println("进⾏字符统计中");CensusChar();System.out.println("字符统计完毕");}public void CensusChar() throws IOException{int intchar;FileInputStream fis = new FileInputStream(file);System.out.println("统计中");//这种统计处理⽅案,速度极慢,不建议使⽤,以下采⽤缓存读数据。
哈夫曼的文件压缩解压程序
哈夫曼的文件压缩解压程序文件压缩是在计算机领域中常见的操作之一。
在处理大量数据时,压缩文件可以有效地减少存储空间和传输时间。
而哈夫曼编码是一种常用的无损压缩算法,它通过将出现频率较高的字符编码为较短的比特序列,而将出现频率较低的字符编码为较长的比特序列,从而实现了对文件的高效压缩和解压缩。
哈夫曼编码的基本原理是构建一棵哈夫曼树。
哈夫曼树是一种特殊的二叉树,它的叶子节点对应于文件中出现的字符,而每个字符的出现频率决定了其在树中的位置。
构建哈夫曼树的过程包括以下几个步骤:1. 统计文件中每个字符的出现频率。
2. 将每个字符及其频率构建为节点。
3. 将这些节点按照频率从小到大排列。
4. 依次选取频率最低的两个节点,合并为一个新节点,并将其频率设置为两个节点频率之和。
5. 将新节点插入到节点列表中,并保持列表有序。
6. 重复步骤4和步骤5,直到节点列表中只剩下一个节点,这个节点即为哈夫曼树的根节点。
通过构建出的哈夫曼树,可以为每个字符生成对应的哈夫曼编码。
哈夫曼编码的生成规则是:根节点到叶子节点的路径上,左孩子节点表示编码为0,右孩子节点表示编码为1。
这样,所有的字符都可以通过对应的哈夫曼编码来进行压缩,并且可以保证解压缩后的文件与原文件完全一致。
接下来我们来实现一个简单的哈夫曼的文件压缩解压程序。
首先,我们需要读取待压缩的文件,并统计每个字符的出现频率。
这可以通过遍历文件中的每个字符,并使用一个字典来记录每个字符出现的次数来实现。
然后,根据字符的频率构建哈夫曼树。
我们可以使用最小堆来维护节点列表,并根据节点的频率进行排序。
每次从堆中取出频率最低的两个节点,合并为一个新节点,并将新节点插入堆中。
当堆中只有一个节点时,停止合并,这个节点即为哈夫曼树的根节点。
生成了哈夫曼树后,我们可以根据树的结构,为每个字符生成对应的哈夫曼编码。
然后,我们将原文件中的每个字符替换为其对应的哈夫曼编码,并将结果写入到压缩文件中。
数据结构中的压缩与解压缩算法
数据结构中的压缩与解压缩算法在数据结构中,压缩与解压缩算法扮演着重要的角色。
它们可以显著减少数据存储和传输所需的空间和时间。
压缩算法使用各种技术来减少数据的大小,而解压缩算法则将压缩的数据还原到其原始状态。
本文将介绍几种常用的压缩与解压缩算法,并讨论它们的原理和应用。
一、哈夫曼编码哈夫曼编码是一种基于变长编码的压缩算法。
它通过根据输入数据中字符的频率来构建一棵哈夫曼树,并生成一个独特的编码表。
在哈夫曼编码中,频率较高的字符用较短的编码表示,而频率较低的字符用较长的编码表示。
这种编码方式可以大大减少数据的大小,并且可以在解压缩时快速还原原始数据。
二、LZW压缩LZW(Lempel-Ziv-Welch)压缩算法是一种基于字典的压缩算法。
它通过在压缩和解压缩过程中动态构建和更新字典,将输入数据中的字符串替换为对应的索引。
LZW压缩算法能够在保持数据质量的同时实现很高的压缩比。
它被广泛应用于图像、音频和视频等多媒体数据的压缩。
三、Run-Length编码Run-Length编码是一种简单但有效的压缩算法。
它通过将连续重复的字符或数据序列替换为一个标记和一个计数值来实现压缩。
例如,连续出现的字符 "AAAABBBCCD" 可以被编码为 "4A3B2C1D"。
Run-Length编码在处理包含大量连续重复数据的情况下非常有效,但对于非重复数据的压缩效果有限。
四、Burrows-Wheeler变换Burrows-Wheeler变换是一种用于数据压缩的重排和重新排列技术。
它通过对输入数据进行循环右移和排序,生成一个新的字符串。
然后,通过记录原始字符串的最后一个字符在排序后的字符串中的位置,以及排序后的字符串中的每个字符前一个字符的索引,可以实现数据的压缩。
解压缩时,通过逆向操作将压缩后的数据还原为原始数据。
以上介绍了几种常用的压缩与解压缩算法,它们在数据结构中起着重要的作用。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
哈夫曼树的压缩与解压1.算法简要描述1.哈夫曼算法1.哈弗曼算法是根据给定的n个权值{w1,w2,w3.......wn},构造由n棵二叉树构成的深林F={T1,T2,。
Tn},其中每个二叉树Ti分别都是只含有一个权值wi的根结点,其左右子树为空(i=1,,,,,,2)。
2.在深林F中选取其根结点的权值最小的两棵二叉树,分别作其左右子树构造一颗新的二叉树,并置这棵新的二叉树根结点的权值为其左右子树的根结点之和。
3.从F中删去这两棵二叉树,同时刚新生成的二叉树加入到深林F中。
4.重复2,3,步骤,直至深林F中只含有一颗二叉树为止。
2.哈夫曼树的实现函数String EnCode(Char Type ch):表示哈夫曼树已存在,返回字符ch的编码。
函数LinkList<CharType>UnCode(String strCode):表示对哈夫曼树进行译码,返回编码前的字符序列。
根据算法可以看出,在具有n个结点权值的哈夫曼树的构造过程中,每次都是从F中删去两棵树,增加一棵树,即每次结束后减少一棵树,经过n-1次处理后,F中就只剩下一棵树了。
另外,每次合并都要产生一个新的结点,合并n-1次后共产生了n-1个新结点,并且这n-1个新节点都是具有左右子树的分支结点。
则最终得到的哈夫曼树中共有2n-1个结点,并且其中没有度为1的分支结点,最后一次产生的新结点就是哈夫曼树的根结点。
源代码中创建了一个哈夫曼树结点类,其中有数据成员weight,parent,leftChild,rightChild分别代表了权值,双亲,左孩子,右孩子。
在哈夫曼树类中有数据成员*nodes,*LeafChars,*LeafCharCodes,curPos,num,分别用来存储结点信息,叶结点字符信息,叶结点字符编码信息,译码时从根结点到叶结点路径的当前结点,叶结点个数。
哈夫曼树类中含有多个函数,有构造函数,析构函数等。
由函数HuffmanTree(CharType ch[],WeightType w[],int n)来构造由字符,权值,和字符个数构造哈夫曼树,在根据哈夫曼算法很容易实现哈夫曼类的函数以及构造函数。
在在算法中,求叶结点字符的编码时,需要从叶结点出发走一条从高叶结点到根结点的路径,而编码却是从根结点出发到叶结点的路径,由左分支为编码0,右分支为编码1,得到的编码,因此从叶结点出发到根结点的路径得到的编码是实际编码的逆序,并且编码长度不确定,又由于可以再线性链表中构造串,因此将编码的信息储存在一个线性立案标准,每得到一位编码都将其插入在线性链表的最前面。
在求某个字符的编码是由函数EnCode(CharType ch)来求,返回字符编码。
在进行译码时,用一个线性链表存储字符序列,由函数Decode(String strCode)来求,对编码串strCode进行译码,返回编码前的字符序列。
函数Compress()用哈夫曼编码压缩文件。
函数Decompress()解压缩用哈夫曼编码压缩的文件。
在主函数中有两个选项,一个是选择编码压缩,一个是解压。
在函数中使用了文件输入输出流,我们可以选择要压缩的文件名输入,在选出压缩文件保存的地方和文件类型,将压缩所得到的文件存储在另一个文件中,解压也是如此。
2.源代码#ifndef __HUFFMAN_TREE_NODE_H__#define __HUFFMAN_TREE_NODE_H__// 哈夫曼树结点类模板template <class WeightType>struct HuffmanTreeNode{WeightType weight; // 权数据域unsigned int parent, leftChild, rightChild; // 双亲,左右孩子域HuffmanTreeNode(); // 无参数的构造函数模板HuffmanTreeNode(WeightType w, int p = 0, int lChild = 0, int rChild = 0);// 已知权,双亲及左右孩子构造结构};// 哈夫曼树结点类模板的实现部分template <class WeightType>HuffmanTreeNode<WeightType>::HuffmanTreeNode()// 操作结果:构造空结点{parent = leftChild = rightChild = 0;}template <class WeightType>HuffmanTreeNode<WeightType>::HuffmanTreeNode(WeightType w, int p, int lChild, int rChild) // 操作结果:构造一个权为w,双亲为p,左孩子为lChild,右孩子为rChild的结点{weight = w; // 权parent = p; // 双亲leftChild = lChild; // 左孩子rightChild = rChild; // 右孩子}#endif#ifndef __HUFFMAN_TREE_H__#define __HUFFMAN_TREE_H__#include"string.h"// 串类#include"huffman_tree_node.h"// 哈夫曼树结点类模板// 哈夫曼树类模板template <class CharType, class WeightType>class HuffmanTree{protected:HuffmanTreeNode<WeightType> *nodes; // 存储结点信息,nodes[0]未用CharType *LeafChars; // 叶结点字符信息,LeafChars[0]未用String *LeafCharCodes; // 叶结点字符编码信息,LeafCharCodes[0]未用int curPos; // 译码时从根结点到叶结点路径的当前结点int num; // 叶结点个数// 辅助函数模板:void Select(int cur, int &r1, int &r2); // nodes[1 ~ cur]中选择双亲为,权值最小的两个结点r1,r2void CreatHuffmanTree(CharType ch[], WeightType w[], int n);// 由字符,权值和字符个数构造哈夫曼树public:// 哈夫曼树方法声明及重载编译系统默认方法声明:HuffmanTree(CharType ch[], WeightType w[], int n); // 由字符,权值和字符个数构造哈夫曼树virtual ~HuffmanTree(); // 析构函数模板String Encode(CharType ch); // 编码LinkList<CharType> Decode(String strCode); // 译码HuffmanTree(const HuffmanTree<CharType, WeightType> ©); // 复制构造函数模板HuffmanTree<CharType, WeightType> &operator=(const HuffmanTree<CharType, WeightType>& copy); // 重载赋值运算符};// 孩子兄弟表示哈夫曼树类模板的实现部分template <class CharType, class WeightType>void HuffmanTree<CharType, WeightType>::Select(int cur, int &r1, int &r2)// 操作结果:nodes[1 ~ cur]中选择双亲为,权值最小的两个结点r1,r2{r1 = r2 = 0; // 0表示空结点for (int pos = 1; pos <= cur; pos++){ // 查找树值最小的两个结点if (nodes[pos].parent != 0) continue; // 只处理双亲不为的结点if (r1 == 0){r1 = pos; // r1为空,将pos赋值给r1}else if (r2 == 0){r2 = pos; // r2为空,将pos赋值给r2}else if(nodes[pos].weight < nodes[r1].weight){r1 = pos; // nodes[pos]权值比nodes[r1]更小,将pos赋为r1}else if (nodes[pos].weight < nodes[r2].weight){r2 = pos; // nodes[pos]权值比nodes[r2]更小,将pos赋为r2}}}void HuffmanTree<CharType, WeightType>::CreatHuffmanTree(CharType ch[], WeightType w[], int n)// 操作结果:由字符,权值和字符个数构造哈夫曼树{num = n; // 叶结点个数int m = 2 * n - 1; // 结点个数nodes = new HuffmanTreeNode<WeightType>[m + 1]; // nodes[0]未用LeafChars = new CharType[n + 1]; // LeafChars[0]未用LeafCharCodes = new String[n + 1]; // LeafCharCodes[0]未用int pos; // 临时变量for (pos = 1; pos <= n; pos++){ // 存储叶结点信息nodes[pos].weight = w[pos - 1]; // 权值LeafChars[pos] = ch[pos - 1]; // 字符}for (pos = n + 1; pos <= m; pos++){ // 建立哈夫曼树int r1, r2;Select(pos - 1, r1, r2); // nodes[1 ~ pos - 1]中选择双亲为,权值最小的两个结点r1,r2// 合并以r1,r2为根的树nodes[r1].parent = nodes[r2].parent = pos; // r1,r2双亲为posnodes[pos].leftChild = r1; // r1为pos的左孩子nodes[pos].rightChild = r2; // r2为pos的右孩子nodes[pos].weight = nodes[r1].weight + nodes[r2].weight;//pos的权为r1,r2的权值之和}for (pos = 1; pos <= n; pos++){ // 求n个叶结点字符的编码LinkList<char> charCode; // 暂存叶结点字符编码信息for (unsigned int child = pos, parent = nodes[child].parent; parent != 0;child = parent, parent = nodes[child].parent){ // 从叶结点到根结点逆向求编码if (nodes[parent].leftChild == child) charCode.Insert(1, '0');// 左分支编码为'0'else charCode.Insert(1, '1'); // 右分支编码为'1'}LeafCharCodes[pos] = charCode; // charCode中存储字符编码}curPos = m; // 译码时从根结点开始,m为根}HuffmanTree<CharType, WeightType>::HuffmanTree(CharType ch[], WeightType w[], int n) // 操作结果:由字符,权值和字符个数构造哈夫曼树{CreatHuffmanTree(ch, w, n); // 由字符,权值和字符个数构造哈夫曼树}template <class CharType, class WeightType>HuffmanTree<CharType, WeightType>::~HuffmanTree()// 操作结果:销毁哈夫曼树{if (nodes != NULL) delete []nodes; // 释放结点信息if (LeafChars != NULL) delete []LeafChars; // 释放叶结点字符信息if (LeafCharCodes != NULL) delete []LeafCharCodes; // 释放叶结点字符编码信息}template <class CharType, class WeightType>String HuffmanTree<CharType, WeightType>::Encode(CharType ch)// 操作结果:返回字符编码{for (int pos = 1; pos <= num; pos++){ // 查找字符的位置if (LeafChars[pos] == ch) return LeafCharCodes[pos];// 找到字符,得到编码}throw Error("非法字符,无法编码!"); // 抛出异常}template <class CharType, class WeightType>LinkList<CharType> HuffmanTree<CharType, WeightType>::Decode(String strCode)// 操作结果:对编码串strCode进行译码,返回编码前的字符序列{LinkList<CharType> charList; // 编码前的字符序列for (int pos = 0; pos < strCode.Length(); pos++){ // 处理每位编码if (strCode[pos] == '0') curPos = nodes[curPos].leftChild; // '0'表示左分支else curPos = nodes[curPos].rightChild; // '1'表示左分支if (nodes[curPos].leftChild == 0 && nodes[curPos].rightChild == 0){ // 译码时从根结点到叶结点路径的当前结点为叶结点charList.Insert(charList.Length() + 1, LeafChars[curPos]);curPos = 2 * num - 1; // curPos回归根结点}}return charList; // 返回编码前的字符序列}template <class CharType, class WeightType>HuffmanTree<CharType, WeightType>::HuffmanTree(const HuffmanTree<CharType, WeightType> ©)// 操作结果:由哈夫曼树copy构造新哈夫曼树——复制构造函数模板{num = copy.num; // 叶结点个数curPos = copy.curPos; // 译码时从根结点到叶结点路径的当前结点int m = 2 * num - 1; // 结点总数nodes = new HuffmanTreeNode<WeightType>[m + 1]; // nodes[0]未用LeafChars = new CharType[num + 1]; // LeafChars[0]未用LeafCharCodes = new String[num + 1]; // LeafCharCodes[0]未用int pos; // 临时变量for (pos = 1; pos <= m; pos++){ // 复制结点信息nodes[pos] = copy.nodes[pos]; // 结点信息}for (pos = 1; pos <= num; pos++){ // 复制叶结点字符信息与叶结点字符编码信息LeafChars[pos] = copy.LeafChars[pos]; // 叶结点字符信息LeafCharCodes[pos] = copy.LeafCharCodes[pos];// 叶结点字符编码信息}}template <class CharType, class WeightType>HuffmanTree<CharType, WeightType> &HuffmanTree<CharType,WeightType>::operator=(const HuffmanTree<CharType, WeightType>& copy)// 操作结果:将哈夫曼树copy赋值给当前哈夫曼树——重载赋值运算符{if (© != this){if (nodes != NULL) delete []nodes; // 释放结点信息if (LeafChars != NULL) delete []LeafChars; // 释放叶结点字符信息if (LeafCharCodes != NULL) delete []LeafCharCodes; // 释放叶结点字符编码信息num = copy.num; // 叶结点个数curPos = copy.curPos; // 译码时从根结点到叶结点路径的当前结点int m = 2 * num - 1; // 结点总数nodes = new HuffmanTreeNode<WeightType>[m + 1]; // nodes[0]未用LeafChars = new CharType[num + 1]; // LeafChars[0]未用LeafCharCodes = new String[num + 1]; // LeafCharCodes[0]未用int pos; // 临时变量for (pos = 1; pos <= m; pos++){ // 复制结点信息nodes[pos] = copy.nodes[pos]; // 结点信息}for (pos = 1; pos <= num; pos++){ // 复制叶结点字符信息与叶结点字符编码信息LeafChars[pos] = copy.LeafChars[pos]; // 叶结点字符信息LeafCharCodes[pos] = copy.LeafCharCodes[pos];// 叶结点字符编码信息}}return *this;}#endif#ifndef __HUFFMAN_COMPRESS_H__#define __HUFFMAN_COMPRESS_H__#include"huffman_tree.h"// 哈夫曼树类struct BufferType// 字符缓存器{char ch; // 字符unsigned int bits; // 实际比特数};class HuffmanCompress// 哈夫曼压缩类{protected:HuffmanTree<char, unsigned long> *pHuffmanTree;FILE *infp,*outfp; // 输入/出文件BufferType buf; // 字符缓存void Write(unsigned int bit); // 向目标文件中写入一个比特void WriteToOutfp(); // 强行将字符缓存写入目标文件public:HuffmanCompress(); // 无参数的构造函数~HuffmanCompress(); // 析构函数void Compress(); // 压缩算法void Decompress(); // 解压缩算法HuffmanCompress(const HuffmanCompress ©); // 复制构造函数HuffmanCompress &operator=(const HuffmanCompress& copy);// 赋值运算符};HuffmanCompress::HuffmanCompress(){pHuffmanTree = NULL; // 空哈夫曼树}HuffmanCompress::~HuffmanCompress(){if (pHuffmanTree != NULL)delete []pHuffmanTree; // 释放空间}void HuffmanCompress::Write(unsigned int bit) // 操作结果:向目标文件中写入一个比特{buf.bits++; // 缓存比特数自增buf.ch = (buf.ch << 1) | bit; // 将bit加入到缓存字符中if (buf.bits == 8){ // 缓存区已满,写入目标文件fputc(buf.ch, outfp); // 写入目标文件buf.bits = 0; // 初始化bitsbuf.ch = 0; // 初始化ch}}void HuffmanCompress::WriteToOutfp() // 操作结果:强行将字符缓存写入目标文件{unsigned int len = buf.bits; // 缓存实际比特数if (len > 0){ // 缓存非空, 将缓存的比特个数增加到, 自动写入目标文件for (unsigned int i = 0; i < 8 - len; i++) Write(0);}}void HuffmanCompress::Compress()// 操作结果:用哈夫曼编码压缩文件{char infName[256], outfName[256]; // 输入(源)/出(目标)文件名cout << "请输入源文件名(文件小于GB):"; // 被压缩文件小于GBcin >> infName; // 输入源文件名if ((infp = fopen(infName, "r+b")) == NULL)throw Error("打开源文件失败!"); // 抛出异常fgetc(infp); // 取出源文件第一个字符if (feof(infp))throw Error("空源文件!"); // 抛出异常cout << "请输入目标文件:";cin >> outfName;if ((outfp = fopen(outfName, "w+b")) == NULL)throw Error("打开目标文件失败!"); // 抛出异常cout << "正在处理,请稍候..." << endl;const unsigned long n = 256; // 字符个数char ch[n]; // 字符数组unsigned long w[n]; // 字符出现频度(权) unsigned long i, size = 0;char cha;for (i = 0; i < n; i++){ // 初始化ch[]和w[]ch[i] = (char)i; // 初始化ch[i]w[i] = 0; // 初始化w[i]}rewind(infp); // 使源文件指针指向文件开始处cha = fgetc(infp); // 取出源文件第一个字符while (!feof(infp)){ // 统计字符出现频度w[(unsigned char)cha]++; // 字符cha出现频度自加size++; // 文件大小自加cha=fgetc(infp); // 取出源文件下一个字符}if (pHuffmanTree != NULL) delete []pHuffmanTree; // 释放空间pHuffmanTree = new HuffmanTree<char, unsigned long>(ch, w, n); // 生成哈夫曼树rewind(outfp); // 使目标文件指针指向文件开始处fwrite(&size, sizeof(unsigned long), 1, outfp); // 向目标文件写入源文件大小for (i = 0; i < n; i++){ // 向目标文件写入字符出现频度fwrite(&w[i], sizeof(unsigned long), 1, outfp);}buf.bits = 0; // 初始化bitsbuf.ch = 0; // 初始化chrewind(infp); // 使源文件指针指向文件开始处cha = fgetc(infp); // 取出源文件的第一个字符while (!feof(infp)){ // 对源文件字符进行编码,并将编码写入目标文件String strTmp = pHuffmanTree->Encode(cha);// 字符编码for (i = 0; i<strTmp.Length(); i++){ // 向目标文件写入编码if (strTmp[i] == '0')Write(0); // 向目标文件写入else Write(1); // 向目标文件写入}cha = fgetc(infp); // 取出源文件的下一个字符}WriteToOutfp(); // 强行写入目标文件fclose(infp); fclose(outfp); // 关闭文件cout << "处理结束." << endl;}void HuffmanCompress::Decompress()// 操作结果:解压缩用哈夫曼编码压缩的文件{char infName[256], outfName[256]; // 输入(压缩)/出(目标)文件名cout << "请输入压缩文件名:";cin >> infName;if ((infp = fopen(infName, "r+b")) == NULL)throw Error("打开压缩文件失败!"); // 抛出异常fgetc(infp); // 取出压缩文件第一个字符if (feof(infp))throw Error("压缩文件为空!"); // 抛出异常cout << "请输入目标文件名:";cin >> outfName;if ((outfp = fopen(outfName, "w+b")) == NULL)throw Error("打开目标文件失败!"); // 抛出异常cout << "正在处理,请稍候..." << endl;const unsigned long n = 256; // 字符个数char ch[n]; // 字符数组unsigned long w[n]; // 权unsigned long i, size = 0;char cha;rewind(infp); // 使源文件指针指向文件开始处fread(&size, sizeof(unsigned long), 1, infp); // 读取目标文件的大小for (i = 0; i < n; i++){ch[i] = (char)i; // 初始化ch[i]fread(&w[i], sizeof(unsigned long), 1, infp); // 读取字符频度}if (pHuffmanTree != NULL)delete []pHuffmanTree; // 释放空间pHuffmanTree = new HuffmanTree<char, unsigned long>(ch, w, n); // 生成哈夫曼树unsigned long len = 0; // 解压的字符数cha = fgetc(infp); // 取出源文件的第一个字符while (!feof(infp)){ // 对压缩文件字符进行解码,并将解码的字符写入目标文件String strTmp = ""; // 将cha转换二进制形式的串unsigned char c = (unsigned char)cha; // 将cha转换成unsigned char类型for (i = 0; i < 8; i++){ // 将c转换成二进制串if (c < 128) Concat(strTmp, "0"); // 最高位为else Concat(strTmp, "1"); // 最高位为c = c << 1; // 左移一位}LinkList<char> lkText = pHuffmanTree->Decode(strTmp);// 译码String strTemp(lkText);for (i = 1; i<=strTemp.Length(); i++){ // 向目标文件写入字符len++; // 目标文件长度自加fputc(strTemp[i-1], outfp); // 将字符写入目标文件中if (len == size) break; // 解压完毕退出内循环}if (len == size) break; // 解压完毕退出外循环cha = fgetc(infp); // 取出源文件的下一个字符}fclose(infp); fclose(outfp); // 关闭文件cout << "处理结束." << endl;}HuffmanCompress::HuffmanCompress(const HuffmanCompress ©)// 操作结果:由哈夫曼压缩类对象copy构造新哈夫曼压缩类对象——复制构造函数{if (copy.pHuffmanTree != NULL){ // copy为非空哈夫曼压缩类对象pHuffmanTree = new HuffmanTree<char, unsigned long>(*copy.pHuffmanTree);// 生成哈夫曼树}else pHuffmanTree = NULL; // 生成空哈夫曼压缩类对象}HuffmanCompress &HuffmanCompress::operator=(const HuffmanCompress& copy)// 操作结果:将哈夫曼压缩类对象copy赋值给当前哈夫曼压缩类对象——赋值运算符{if (© != this){if (pHuffmanTree != NULL) delete []pHuffmanTree; // 释放空间if (copy.pHuffmanTree != NULL){ // copy为非空哈夫曼压缩类对象pHuffmanTree = new HuffmanTree<char, unsigned long>(*copy.pHuffmanTree);// 生成哈夫曼树}else pHuffmanTree = NULL; // 生成空哈夫曼压缩类对象}return *this;}#endif#include"utility.h"// 实用程序软件包#include"huffman_compress.h"// 哈夫曼压缩算法int main(void){try// 用try封装可能出现异常的代码{HuffmanCompress obj;int select = 0;while (select != 3){cout << endl << "1. 压缩";cout << endl << "2. 解压";cout << endl << "3. 退出";cout << endl << "请选择:";cin >> select; // 输入选择while (cin.get() != '\n'); // 忽略用户输入的其他字符switch (select){case 1:press(); // 压缩break;case 2:obj.Decompress(); // 解压缩}}}catch (Error err) // 捕捉并处理异常{err.Show(); // 显示异常信息}system("PAUSE"); // 调用库函数system()return 0; // 返回值, 返回操作系统}3.测试结果由于测试的文件字符太多,则就截下来一部分的压缩后的编码。