哈夫曼的文件压缩解压程序

合集下载

哈夫曼编解码压缩解压文件—C++实现

哈夫曼编解码压缩解压文件—C++实现

哈夫曼编解码压缩解压⽂件—C++实现前⾔哈夫曼编码是⼀种贪⼼算法和⼆叉树结合的字符编码⽅式,具有⼴泛的应⽤背景,最直观的是⽂件压缩。

本⽂主要讲述如何⽤哈夫曼编解码实现⽂件的压缩和解压,并给出代码实现。

哈夫曼编码的概念哈夫曼树⼜称作最优树,是⼀种带权路径长度最短的树,⽽通过哈夫曼树构造出的编码⽅式称作哈夫曼编码。

也就是说哈夫曼编码是⼀个通过哈夫曼树进⾏的⼀种编码,⼀般情况下,以字符 “0” 与 “1” 表⽰。

编码的实现过程很简单,只要实现哈夫曼树,通过遍历哈夫曼树,这⾥我们从根节点开始向下遍历,如果下个节点是左孩⼦,则在字符串后⾯追加 “0”,如果为其右孩⼦,则在字符串后追加 “1”。

结束条件为当前节点为叶⼦节点,得到的字符串就是叶⼦节点对应的字符的编码。

哈夫曼树实现根据贪⼼算法的思想实现,把字符出现频率较多的字符⽤稍微短⼀点的编码,⽽出现频率较少的字符⽤稍微长⼀点的编码。

哈夫曼树就是按照这种思想实现,下⾯将举例分析创建哈夫曼树的具体过程。

下⾯表格的每⼀⾏分别对应字符及出现频率,根据这些信息就能创建⼀棵哈夫曼树。

字符出现频率编码总⼆进制位数a5001500b25001500c120001360d600001240e3000001150f2000000100如下图,将每个字符看作⼀个节点,将带有频率的字符全部放到优先队列中,每次从队列中取频率最⼩的两个节点 a 和b(这⾥频率最⼩的 a 作为左⼦树),然后新建⼀个节点R,把节点设置为两个节点的频率之和,然后把这个新节点R作为节点A和B的⽗亲节点。

最后再把R放到优先队列中。

重复这个过程,直到队列中只有⼀个元素,即为哈夫曼树的根节点。

由上分析可得,哈夫曼编码的需要的总⼆进制位数为 500 + 500 + 360 + 240 + 150 + 100 = 1850。

上⾯的例⼦如果⽤等长的编码对字符进⾏压缩,实现起来更简单,6 个字符必须要 3 位⼆进制位表⽰,解压缩的时候每次从⽂本中读取 3 位⼆进制码就能翻译成对应的字符,如 000,001,010,011,100,101 分别表⽰a,b,c,d,e,f。

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课题背景及意义近年来,随着计算机技术的发展,多媒体计算机技术、计算机网络技术以及现代多媒体通信技术正在向着信息化、高速化、智能化迅速发展。

Huffman的应用之文件压缩与解压缩

Huffman的应用之文件压缩与解压缩

Huffman的应用之文件压缩与解压缩最近这段时间一直在学习树的这种数据结构,也接触到了Huffman树以及了解了什仫是Huffman编码,而我们常用的zip压缩也是利用的Huffman编码的特性,那仫是不是可以自己实现一个文件压缩呢?当然可以了.在文件压缩中我实现了Huffman树和建堆Heap的代码,zip压缩的介绍>下面开始介绍自己实现的文件压缩的思路和问题...1).统计>读取一个文件统计这个文件中字符出现的次数.2).建树>以字符出现的次数作为权值使用贪心算法构建Huffman树(根据Huffman树的特性>字符出现次数多的一定靠近根结点,出现次数少的一定远离根结点).3).生成Huffman编码>规则左0右1.4).压缩>再次读取文件,根据生成的Huffman编码压缩文件.5).生成配置文件>将字符以及字符出现的次数写进配置文件中.6).解压缩>利用配置文件还原出Huffman树,根据压缩文件还原出原文件.7).测试>判断解压是否正确需要判断原文件和解压缩之后的文件是否相同,利用Beyond Compare软件进行对比.下面是我举的一个简单的范例,模拟压缩和解压缩的过程,希望有读者有帮助利用Beyond Compare软件进行对比>在实现中出现了很多的问题,下面我提出几个容易犯的问题,仅供参考1).在使用贪心算法构建Huffman树的时候,如果我们以unsigned char一个字节来存储它总共有2^8=256个字符,如果将所有的字符都构建Huffman树,这不仅降低了效率还将分配极大的内存.所以我设立了非法值这个概念,只有当字符出现的次数不为0的时候才将该字符构建到Huffman树中.2).在写压缩文件的时候我们是将字符对应的Huffman编码转化为对应的位,每当填满一个字节(8位)后再写入压缩文件中.如果最后一个字节没有填满我们就将已经填的位移位空出后几个位置,将未满的位置补0补满一个字节再写入压缩文件中.3).如果我们将源文件压缩之后再去解压,因为你的Huffman树和Huffman编码都是在压缩函数中得到的,此时再去解压那仫你的Huffman编码以及不存在了该如何去还原文件呢?这就是为什仫要生成配置文件的原因了,在配置文件中写入了字符以及字符出现的次数.在解压缩中根据配置文件构建新的Huffman树.4).由压缩文件还原文件的时候如何知道压了多少个字符呢?也就是说因为我们在压缩的时候最后一个字节是补了0的在解压缩的时候可能会把这个补的位当成字符的编码来处理.一种想法是在统计字符出现的次数的时候设置一个变量,每读取一个字符该变量就加1,最后将该变量写进配置文件中.另外一种想法就是根据根结点的权值,通过上述简单的实例观察可知根结点权值中的_count就是字符出现的次数.解决了以上几个问题,我的程序已经可以压缩那256个字符并正确的还原了,那仫如果是大文件或者是汉字,图片以及音频视频呢?1).因为有些特殊的字符编码,所以我们统计字符出现的次数的时候应该用的是unsigned char,刚开始我用的文件结束标志是EOF在ASII中它的编码是-1此时已经不可以用EOF来判断是否文件结束了,所以我用了feof这个函数来判断文件是否结束.2).统计字符出现次数应该用的类型是long long,这就解决了大文件的压缩和解压缩的问题了.3).因为汉字,图片,视频这些在内存中是以二进制的形式存在的,所以我们将以文本形式打开读或者写的修改为以二进制的形式读或者写.为了验证大文件的压缩我找了一个8.09M的文件压缩之后是6.50M,并且可以正确还原.1).测试效率>2).利用Beyond Compare软件进行对比,如果一样说明压缩成功>[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片#define _CRT_SECURE_NO_W ARNINGS 1#pragma once#include "Heap.h"template<class T>struct HuffmanTreeNode{T _weight;HuffmanTreeNode<T> *_left;HuffmanTreeNode<T> *_right;HuffmanTreeNode<T> *_parent;HuffmanTreeNode(const T& w=T()):_weight(w),_left(NULL),_right(NULL),_parent(NULL){}};template<class T>class HuffmanTree{typedef HuffmanTreeNode<T> Node;public:HuffmanTree():_root(NULL){}HuffmanTree(const T* a,size_t size):_root(NULL){_root=_CreatHuffmanTree(a,size);}//将未出现的字符过滤出来,不构造堆HuffmanTree(const T* a,size_t size,const T& invalid){_root=_CreatHuffmanTree(a,size,invalid);}Node* GetRoot(){return _root;}~HuffmanTree(){_Destroy(_root);}protected:Node *_CreatHuffmanTree(const T* a,size_t size){struct NodeLess{bool operator()(Node *l,Node *r)const{return l->_weight < r->_weight;}};Heap<Node *,NodeLess> minHeap;//建立结点并放入vector中for (size_t i=0;i<size;++i){Node *tmp=new Node(a[i]);minHeap.Push(tmp);}//取出较小的两个结点作为左右孩子并构建父结点while (minHeap.Size() > 1){Node *left=minHeap.Top();minHeap.Pop();Node *right=minHeap.Top();minHeap.Pop();Node *parent=new Node(left->_weight + right->_weight);parent->_left=left;parent->_right=right;left->_parent=parent;right->_parent=parent;minHeap.Push(parent);}return minHeap.Top();}//思路类似不带过滤功能的Node *_CreatHuffmanTree(const T* a,size_t size,const T& invalid) {struct NodeLess{bool operator()(Node *l,Node *r)const{return l->_weight < r->_weight;}};Heap<Node *,NodeLess> minHeap;//建立结点并放入vector中for (size_t i=0;i<size;++i){if(a[i] != invalid){Node *tmp=new Node(a[i]);minHeap.Push(tmp);}}//取出较小的两个结点作为左右孩子并构建父结点while (minHeap.Size() > 1){Node *left=minHeap.Top();minHeap.Pop();Node *right=minHeap.Top();minHeap.Pop();Node *parent=new Node(left->_weight + right->_weight);parent->_left=left;parent->_right=right;left->_parent=parent;right->_parent=parent;minHeap.Push(parent);}return minHeap.Top();}void _Destroy(Node *&root)if(root == NULL)return ;Node *cur=root;if(cur){_Destroy(cur->_left);_Destroy(cur->_right);delete cur;cur=NULL;return ;}}protected:Node *_root;};void testHuffmanTree(){int a[]={0,1,2,3,4,5,6,7,8,9};int size=sizeof(a)/sizeof(a[0]);HuffmanTree<int> ht(a,size);}[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片#define _CRT_SECURE_NO_W ARNINGS 1#pragma once//利用仿函数的特性实现代码的复用性template<class T>struct Small{bool operator()(const T& l,const T& r){return l < r;}};template<class T>struct Large{bool operator()(const T& l,const T& r)return l > r;}};template<class T,class Compare=Large<T>> //缺省是建小堆class Heap{public:Heap(){}Heap(const T *a,int size){assert(a);_a.reserve(size);for (int i=0;i<size;++i){_a.push_back(a[i]);}//建堆的时候从倒数第一个非叶子结点开始.for (int j=(size-2)/2;j>=0;--j){_AdjustDown(j);}}void Push(const T& x){_a.push_back(x);_AdjustUp(_a.size()-1);}void Pop(){assert(!_a.empty());swap(_a[0],_a[_a.size()-1]);_a.pop_back();_AdjustDown(0);}size_t Size(){return _a.size();}bool Empty(){return _a.empty();const T& Top()const{assert(!_a.empty());return _a[0];}void Display(){for (size_t i=0;i<_a.size();++i){cout<<_a[i]<<" ";}cout<<endl;}protected:void _AdjustDown(int root){int parent=root;size_t child=2*root+1;while (child < _a.size()){Compare com;//child指向左右孩子中较大的那个数//if (child+1 < _a.size()// && _a[child+1] > _a[child])if(child+1 < _a.size()&& com(_a[child+1],_a[child])){child++;}//if (_a[child] > _a[parent])if(com(_a[child],_a[parent])){swap(_a[child],_a[parent]);parent=child;//初始的child默认指向左孩子child=2*parent+1;}elsebreak;}}void _AdjustUp(int child){while (child > 0){int parent=(child-1)/2;Compare com;//if (_a[child] > _a[parent])if(com(_a[child],_a[parent])){swap(_a[child],_a[parent]);child=parent;}else//插入的数据比父节点的数据域小break;}}protected:vector<T> _a;};//利用堆解决优先级队列的问题template<class T,class Compare=Large<T>>class PriorityQueue{public:PriorityQueue(int *a,int size):_pq(a,size){}void Push(const T& x){_pq.Push(x);}void Pop(){_pq.Pop();}const T& Top()const{return _pq.Top();}void Display(){_pq.Display();}protected:Heap<T,Compare> _pq;};[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片#define _CRT_SECURE_NO_W ARNINGS 1#pragma once#include "HuffmanTree.h"typedef long long Type;struct CharInfo{unsigned char _ch; //出现的字符Type _count; //统计次数string _code; //Huffman编码CharInfo(Type count=0):_ch(0),_count(count),_code(""){}//重载对应的操作符CharInfo operator + (const CharInfo& fc)const{return CharInfo(_count + fc._count);}bool operator != (const CharInfo fc)const{return _count != fc._count;}bool operator < (const CharInfo& fc)const{return _count < fc._count;}};class FileCompress{public://默认的构造函数FileCompress(){for(size_t i=0;i<256;++i){_infos[i]._ch=i;}}string Compress(const char *filename){assert(filename);FILE *pf=fopen(filename,"rb");assert(pf);unsigned char ch=fgetc(pf);//统计字符出现的次数while (!feof(pf)){_infos[ch]._count++;ch=fgetc(pf);}//以该字符出现的次数构建一颗HuffmanTree.CharInfo invalid; //非法值HuffmanTree<CharInfo> ht(_infos,256,invalid);//生成Huffman编码string code;_CreatHuffmanCode(ht.GetRoot(),code);//_CreatHuffmanCode(ht.GetRoot());//压缩文件fseek(pf,0,SEEK_SET); //回到文件头string compressfile=filename;compressfile += ".compress"; //压缩后的文件名FILE *fin=fopen(compressfile.c_str(),"wb");assert(fin);size_t pos=0; //记录位数unsigned char value=0;ch=fgetc(pf);while (!feof(pf)){string &code=_infos[ch]._code;for (size_t i=0;i<code.size();++i){value <<= 1;if(code[i] == '1')value |= 1;elsevalue |= 0; //do-nothing++pos;if(pos == 8) //满一个字节{fputc(value,fin);value=0;pos=0;}}ch=fgetc(pf);}if(pos) //解决不足8位的情况.{value <<= (8-pos);fputc(value,fin);}//配置文件--便于重建Huffman树string ename=filename;configfilename += ".config";FILE *finconfig=fopen(configfilename.c_str(),"wb");assert(finconfig);string line;char buff[128];for (size_t i=0;i<256;++i){//一行一行的读if (_infos[i]._count){line += _infos[i]._ch;line += ",";line += _itoa(_infos[i]._count,buff,10);line += "\n";//fputs(line.c_str(),finconfig);fwrite(line.c_str(),1,line.size(),finconfig);line.clear();}}fclose(pf);fclose(fin);fclose(finconfig);return compressfile;}string UnCompress(const char *filename){assert(filename);string configfilename=filename;size_t index=configfilename.rfind(".");configfilename=configfilename.substr(0,index); configfilename += ".config";FILE *foutconfig=fopen(configfilename.c_str(),"rb"); assert(foutconfig);string line;//读取配置文件--获取字符出现的次数unsigned char ch=0;while (ReadLine(foutconfig,line)){if(line.empty()){line += '\n';continue;}//读到空行ch=line[0];_infos[ch]._count = atoi(line.substr(2).c_str());line.clear();}//构建Huffman树CharInfo invalid;HuffmanTree<CharInfo> hft(_infos,256,invalid);//根结点的权值也就是字符出现的次数总和HuffmanTreeNode<CharInfo> *root=hft.GetRoot(); Type charcount=root->_weight._count;//解压缩string uncompressfilename=filename;index=uncompressfilename.rfind("."); uncompressfilename=uncompressfilename.substr(0,index); uncompressfilename += ".uncompress";FILE *fin=fopen(uncompressfilename.c_str(),"wb"); assert(fin);//由压缩文件还原文件string compressfilename=filename;FILE *fout=fopen(compressfilename.c_str(),"rb");assert(fout);HuffmanTreeNode<CharInfo> *cur=root;int pos=7;ch=fgetc(fout);while (charcount > 0){while (cur){if(cur->_left == NULL && cur->_right == NULL){//叶子结点fputc(cur->_weight._ch,fin);cur=root;--charcount;if (charcount == 0) //所有的字符都处理完成break;}if (ch & (1 << pos)) //检查字符的每个位cur=cur->_right; //1向右走elsecur=cur->_left; //0向左走--pos;if(pos < 0) //一个字节解压完成{ch=fgetc(fout);pos=7;}}}fclose(foutconfig);fclose(fin);fclose(fout);return uncompressfilename;}//读取一行字符并放在line中bool ReadLine(FILE *fout,string& line){int ch=fgetc(fout);if(ch == EOF)return false;while (ch != EOF && ch != '\n'){line += ch;ch=fgetc(fout);}return true;}protected://递归的方法求HuffmanTreeCodevoid _CreatHuffmanCode(HuffmanTreeNode<CharInfo> *root,string &code) {if(root == NULL)return ;_CreatHuffmanCode(root->_left,code+'0');_CreatHuffmanCode(root->_right,code+'1');if(root->_left == NULL && root->_right == NULL) //叶子结点{_infos[root->_weight._ch]._code=code;}}//非递归求HuffmanTreeCodevoid _CreatHuffmanCode(HuffmanTreeNode<CharInfo> *root){if(root == NULL)return ;_CreatHuffmanCode(root->_left);_CreatHuffmanCode(root->_right);if(root->_left == NULL && root->_right == NULL) //叶子结点{string& code=_infos[root->_weight._ch]._code;HuffmanTreeNode<CharInfo> *cur=root;HuffmanTreeNode<CharInfo> *parent=root->_parent;while (parent){if(parent->_left == cur)code.push_back('0'); //左0elsecode.push_back('1'); //右1cur=parent;parent=cur->_parent;}//编码是从根到叶子结点的,需要逆置reverse(code.begin(),code.end());}}protected:CharInfo _infos[256];};void testFileCompress(){FileCompress fc;cout<<"开始压缩"<<endl;cout<<"压缩用时:";int start=GetTickCount();press("2.png"); //input input.BIG 3.mp3int end=GetTickCount();cout<<end-start<<endl;cout<<"开始解压"<<endl;cout<<"解缩用时:";start=GetTickCount();fc.UnCompress("press"); //press press 3.mp3end=GetTickCount();cout<<end-start<<endl;}void testFileUncompress(){FileCompress fc;cout<<"开始解压"<<endl;cout<<"解缩用时:";int start=GetTickCount();fc.UnCompress("2.png");int end=GetTickCount();cout<<end-start<<endl;}经过测试这个小项目已经可以压缩并还原一些文件了,目前还没有发现什仫大的Bug,如果有童鞋发现请第一时间告诉我哦...。

利用哈夫曼编码实现压缩和解压缩

利用哈夫曼编码实现压缩和解压缩

利用哈夫曼编码实现压缩和解压缩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语言实现的文件压缩和解压缩的源代码。

哈夫曼算法的主要思想是:①首先遍历要处理的字符串,得到每个字符的出现的次数;②将每个字符(以其出现次数为权值)分别构造为二叉树(注意此时的二叉树只有一个节点);③取所有二叉树种种字符出现次数最小的二叉树合并为一颗新的二叉树,新二叉树根节点的权值等于两个子节点的权值之和,新节点中的字符忽略;④重复过程③直到所有树被合并为同一棵二叉树⑤遍历最后得到的二叉树,自顶向下按路径编号,指向左节点的边编号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 类型的位缓冲中,可能多个编码才能填满一个位缓冲,每填满一次,将位缓冲区以单个字节的形式写入文件。

使用huffman编码压缩与解压缩(python)

使用huffman编码压缩与解压缩(python)

使⽤huffman编码压缩与解压缩(python)⼀、huffman 编码1.1 huffman 编码介绍哈夫曼编码(Huffman Coding),⼜称霍夫曼编码,是⼀种编码⽅式,哈夫曼编码是可变字长编码(VLC)的⼀种。

Huffman于1952年提出⼀种编码⽅法,该⽅法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,⼀般就叫做Huffman编码(有时也称为霍夫曼编码)huffman 编码是最优码,也是即时码(证明较为复杂,在此不给出证明)1.2 huffman 编码此处介绍⼆元 huffman 编码给定⼀个字符集合 S={s0,s1,⋯,s q},每个字符的概率为 P={p0,p1,⋯,p q}将字符集合按概率由⼤到⼩重新排列,即使得p i≥p i+1将最末尾的两个字符s q−1和s q合并,记为s′,s′的概率为p′=p q−1+p q如果剩余的字符数不为 1,则回到第 1 步huffman 编码的过程⼤致就上述三步当编码完毕后,会构建出⼀棵码树,每个码字的编码可以从码树中获得举个简单的例⼦,对于字符集合 S={A,B,C},概率为 P=0.5,0.3,0.2将B,C合并,记为BC,p(BC)=p(B)+p(C)=0.3+0.2=0.5然后将A,BC合并,记为ABC,p(ABC)=p(A)+p(BC)=0.5+0.5=1记根节点 ABC 码字为空,从根节点向下遍历,往左⾛码字末尾添0,往右⾛添1,那么 A, B, C 的码字分别为 0, 10, 11,编码表就是 {A:0,B:10,C:11}1.3 huffman 编程实现见后⽂的代码实现1.4 测试⾸先在代码⽬录下新建⼀个newfile.txt,⾥边写⼊ABCDEFG下⾯的代码实现了读取newfile.txt并对其压缩,将结果输出⾄output.enc然后对output.enc进⾏解压缩,将解压缩结果输出⾄output.dec# 读⼊⽂件with open('newfile.txt', 'rb') as fp_in:str_bytes = fp_in.read()# 构建huffman编码fre_dic = bytes_fre(str_bytes)huffman_dic = build(fre_dic)# 对⽂本进⾏编码str_enc, padding = encode(str_bytes, huffman_dic, False)# 输出⾄⽂件 output.encwith open('output.enc', 'wb') as fp_out:fp_out.write(str_enc)# 对编码后的⽂本进⾏解码str_dec = decode(str_enc, huffman_dic, padding, False)# 输出⾄⽂件 output.decwith open('output.dec', 'wb') as fp_out:fp_out.write(str_dec)# 打印huffman字典和填充位print('huffman_dic:', huffman_dic)print('padding:', padding)观察压缩前和压缩后的⽂件,发现经过我们的程序压缩之后,⽂件⼩了很多使⽤ winhex 打开 newfile.txt, output.enc, output.dec 查看对应的⼗六进制数值观察 newfile.txt 和 output.dec 的⼗六进制数值,发现⼀模⼀样,说明我们的压缩和解压并没有问题,程序正确接下来来分析 output.enc ⽂件的内容output.enc 中的数值为 C1 4E 50,转化成⼆进制为 11000001 01001110 01010000Loading [MathJax]/jax/output/HTML-CSS/jax.js我们将中间构造出的编码表打印出来,可以得到编码表字符A B C D E F G编码11000001010011100101以及填充位 padding 的值为 4我们对 newfile.txt 中的字符 ABCDEFG 进⾏编码,结果为 11 000 001 010 011 100 101。

用哈夫曼树算法写对文件压缩与解压缩代码

用哈夫曼树算法写对文件压缩与解压缩代码

用哈夫曼树算法设计对文件文件的压缩和解压缩的程序怎么写?基本要求:(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言代码

哈夫曼树与文件解压压缩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%。

哈夫曼树与文件解压压缩C言代码

哈夫曼树与文件解压压缩C言代码

哈夫曼树与⽂件解压压缩C⾔代码1.问题描述哈弗曼树的编码与译码—功能:实现对任何类型⽂件的压缩与解码—输⼊:源⽂件,压缩⽂件—输出:解码正确性判定,统计压缩率、编码与解码速度—要求:使⽤边编码边统计符号概率的⽅法(⾃适应Huffman编码)和事先统计概率的⽅法(静态Huffman编码)2.1程序清单程序书签:1.main函数2.压缩函数3.select函数4.encode函数5.解压函数#include#include#include#include#includestruct 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成员⾥,同时给双亲、孩⼦赋初值-1 n=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{hfmnode[i].lchild=hfmnode[i].rchild=-1;hfmnode[i].parent=-1;}//建⽴哈弗曼树for(i=n;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;iprintf("%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;iif(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;。

基于哈夫曼树的文件压缩解压程序-示例文档

基于哈夫曼树的文件压缩解压程序-示例文档

软件课程设计报告基于哈夫曼树的文件压缩/解压程序计算机科学学院××专业××班××号×××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()计算当前时间(精确到毫秒,以字符串返回)。

哈夫曼的文件压缩解压程序

哈夫曼的文件压缩解压程序

哈夫曼的文件压缩解压程序文件压缩是在计算机领域中常见的操作之一。

在处理大量数据时,压缩文件可以有效地减少存储空间和传输时间。

而哈夫曼编码是一种常用的无损压缩算法,它通过将出现频率较高的字符编码为较短的比特序列,而将出现频率较低的字符编码为较长的比特序列,从而实现了对文件的高效压缩和解压缩。

哈夫曼编码的基本原理是构建一棵哈夫曼树。

哈夫曼树是一种特殊的二叉树,它的叶子节点对应于文件中出现的字符,而每个字符的出现频率决定了其在树中的位置。

构建哈夫曼树的过程包括以下几个步骤:1. 统计文件中每个字符的出现频率。

2. 将每个字符及其频率构建为节点。

3. 将这些节点按照频率从小到大排列。

4. 依次选取频率最低的两个节点,合并为一个新节点,并将其频率设置为两个节点频率之和。

5. 将新节点插入到节点列表中,并保持列表有序。

6. 重复步骤4和步骤5,直到节点列表中只剩下一个节点,这个节点即为哈夫曼树的根节点。

通过构建出的哈夫曼树,可以为每个字符生成对应的哈夫曼编码。

哈夫曼编码的生成规则是:根节点到叶子节点的路径上,左孩子节点表示编码为0,右孩子节点表示编码为1。

这样,所有的字符都可以通过对应的哈夫曼编码来进行压缩,并且可以保证解压缩后的文件与原文件完全一致。

接下来我们来实现一个简单的哈夫曼的文件压缩解压程序。

首先,我们需要读取待压缩的文件,并统计每个字符的出现频率。

这可以通过遍历文件中的每个字符,并使用一个字典来记录每个字符出现的次数来实现。

然后,根据字符的频率构建哈夫曼树。

我们可以使用最小堆来维护节点列表,并根据节点的频率进行排序。

每次从堆中取出频率最低的两个节点,合并为一个新节点,并将新节点插入堆中。

当堆中只有一个节点时,停止合并,这个节点即为哈夫曼树的根节点。

生成了哈夫曼树后,我们可以根据树的结构,为每个字符生成对应的哈夫曼编码。

然后,我们将原文件中的每个字符替换为其对应的哈夫曼编码,并将结果写入到压缩文件中。

基于哈夫曼编码的数据压缩解压程序论文

基于哈夫曼编码的数据压缩解压程序论文

合肥学院计算机科学与技术系课程设计报告2010~2011学年第二学期课程C++课程设计课程设计名称基于哈夫曼编码的数据压缩/解压程序学生姓名龚天棚学号**********专业班级网络工程(1)班指导教师项响琴、徐静2011年6月目录一、需求分析...................................................................................................... - 3 -1.1课程设计目的.......................................................................................... - 3 -1.2课程设计名称及内容.............................................................................. - 3 -1.3任务和要求............................................................................................. - 3 -二、算法设计...................................................................................................... - 4 -2.1设计思想:.............................................................................................. - 4 -2.2 算法思想:............................................................................................. - 5 -2.3 主要模块说明......................................................................................... - 5 -2.4 部分重要函数的实现:......................................................................... - 6 -三、用户手册...................................................................................................... - 6 -四、测试结果...................................................................................................... - 8 -4.1 压缩过程:........................................................................................... - 8 -4.2 解压过程............................................................................................... - 9 -4.3 显示文本内容......................................................................................... - 9 -4.4 显示帮助界面:................................................................................... - 10 -五、总结............................................................................................................ - 10 -六、参考资料..................................................................................................... - 11 -七、附录............................................................................................................ - 12 -7.1源代码.................................................................................................... - 12 -7.2 运行结果............................................................................................... - 22 -一、需求分析1.1课程设计目的将理论教学中涉及到的知识点贯穿起来,对不同的数据类型、程序控制结构、数据结构作一比较和总结,结合设计题目进行综合性应用,对所学知识达到融会贯通的程度。

哈夫曼的文件压缩解压程序课件

哈夫曼的文件压缩解压程序课件
为了提高哈夫曼压缩算法的性能和压缩比,可以采用以 下优化措施
对数据进行预处理,例如去除冗余信息、进行数据格式 化等,以减少需要压缩的数据量。
使用更高效的编码和解码算法,例如使用位操作代替字 符串操作,减少内存占用和计算时间。
采用动态哈夫曼编码技术,根据数据流的特点动态调整 哈夫曼树的结构,以实现更好的压缩效果。
文件压缩原理
文件压缩的概念
01
02
03
文件压缩
通过特定的算法对文件进 行编码,以减少其存储空 间的需求,同时保持文件 完整性和可读性。
压缩文件
经过压缩处理后的文件, 其大小明显小于原始文件 。
解压缩
将压缩文件还原为原始大 小的过程。
文件压缩的原理
数据冗余
大多数文件都包含大量的 数据冗余,如重复的模式 或数据块。
编码
利用数据冗余,通过特定 的算法对数据进行编码, 以减少存储空间需求。
熵编码
根据数据的概率分布进行 编码,使常见数据使用较 短的编码,罕见数据使用 较长的编码。
常见文件压缩算法
Huffman编码
Bzip2
一种基于熵编码的算法,根据数据的 概率分布创建字符到二进制码的映射 。
使用Burrows-Wheeler变换和 Huffman编码的压缩算法,具有较高 的压缩比和较好的数据完整性。
因素。
06
哈夫曼压缩解压程序应用 实例
哈夫曼压缩解压程序的使用场景
1 2
数据存储
哈夫曼压缩解压程序常用于数据存储领域,能够 有效地减小数据文件的大小,节省存储空间。
网络传输
在网络传输中,哈夫曼压缩解压程序能够降低传 输时间和带宽消耗,提高传输效率。
3
实时处理

哈夫曼树解压与压缩

哈夫曼树解压与压缩

资料范本本资料为word版本,可以直接编辑和打印,感谢您的下载哈夫曼树解压与压缩地点:__________________时间:__________________说明:本资料适用于约定双方经过谈判,协商而共同承认,共同遵守的责任与义务,仅供参考,文档可直接下载或修改,不需要的部分可直接删除,使用时请详细阅读内容哈夫曼树的压缩与解压算法简要描述1.哈夫曼算法哈弗曼算法是根据给定的n个权值{w1,w2,w3.......wn},构造由n棵二叉树构成的深林F={T1,T2,。

Tn},其中每个二叉树Ti分别都是只含有一个权值wi的根结点,其左右子树为空(i=1,,,,,,2)。

在深林F中选取其根结点的权值最小的两棵二叉树,分别作其左右子树构造一颗新的二叉树,并置这棵新的二叉树根结点的权值为其左右子树的根结点之和。

从F中删去这两棵二叉树,同时刚新生成的二叉树加入到深林F中。

重复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分别代表了权值,双亲,左孩子,右孩子。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
个操作只需要写出伪代码算法,画出函数的调 用关系图。 4、 调试分析报告
调试过程中遇到的问题并且是如何解决的 以及对设计实现的回顾讨论和分析算法的时空 分析(包括基本操作和主要算法的时空复杂度 的分析)和改进设想经验和体会等
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
二、如何构造最优树
(赫夫曼算法) 以二叉树为例:
(1) 根据给定的 n 个权值 {w1, w2, …, wn}, 构造 n 棵二叉树的集合
F = {T1, T2, … , Tn}, 其中每棵二叉树中均只含一个带权值

wi
的根结点,其左、右子树为空树; 19
D:111
E:011 F:10
G:01011 H:110 26
电文中共使用八种字符,如果按一般方法对其进 行编码,则最少需要3位码长,如:A(000), B(001),C(010),D(011),E(100),F(101), G(110),H(111),假设电文中共100个字符, 按其出现频率则该电文的码长为:3*100=300。
697
7
52
97
13
5 2 67
22
97 52
13
67
29
0
1
13 01
67 00 01
16 01
9 10
07 1
52
110 11231
三、前缀编码
指的是,任何一个字符的编码都 不是同一字符集中另一个字符的编码 的前缀。
利用赫夫曼树可以构造一种不等 长的二进制编码,并且构造所得的赫 夫曼编码是一种最优前缀编码,即使 所传电文的总长度最短。
6
STL组成
三个基本的STL组件: 1)迭代器,提供了访问容器中对象的方法。例如,可以使用一对 迭代器指定list或vector中的一定范围的对象。迭代器就如同一个 指针。事实上,C++的指针也是一种迭代器。但是,迭代器也可 以是那些定义了operator*()以及其他类似于指针的操作符地方法的 类对象。 2)容器,是一种数据结构,如list,vector,和deques ,以模板类 的方法提供。为了访问容器中的数据,可以使用由容器类输出的 迭代器。 3) 算法,是用来操作容器中的数据的模板函数。例如,STL用 sort()来对一个vector中的数据进行排序,用find()来搜索一个list中 的对象。函数本身与他们操作的数据的结构和类型无关,因此他 们可以在从简单数组到高度复杂容器的任何数据结构上使用。
24
练习题
有一份电文中共使用八种字符,频率依 次为: A(0.04),B(0.3),C(0.08),D(0.09), E(0.13),F(0.22),G(0.03),H(0.11),
请构造相应的哈夫曼树(要求树中所有 结点的左右孩子权值必须左大右小),求 出每个字符的哈夫曼编码。
25
解:
设左子树编码为0,右子树编码为1,则 A,B,C,D,E,F,G,H字符的哈夫曼编码为:A: 01010 B:00 C:0100
数据结构课程设计
陈正铭
1
教学目的与要求
《数据结构》课程设计的目的就是要达到理论 与实际应用相结合,使同学们能够根据数据对 象的特性,学会数据组织的方法,能把现实世 界中的实际问题在计算机内部表示出来,并培 养基本的、良好的程序设计技能。
通过这次课程设计,要求掌握数据结构的逻 辑特性和物理表示,数据结构的选择应用、算 法的设计及其实现和性能分析等方面中加深对 课程基本内容的理解。同时,在程序设计方法 以及上机操作等基本技能和科学作风方面受到 比较系统和严格的训练。
该参考教材内容丰富,尤其是C++语言 STL方面的内容,对 日后从事开发设计工作的同学有较大参考作用。但教材价 格较贵,零售价45元,因此本学期没为大家指定定购,大 家可按需购买,但建议购买。
5
STL(Standard Template Library, 标准模板库)概述
STL的一个重要特点是数据结构和算法的分离。尽管 这是个简单的概念,但这种分离确实使得STL变得非 常通用。例如,由于STL的sort()函数是完全通用的, 你可以用它来操作几乎任何数据集合,包括链表,容 器和数组。 STL另一个重要特性是它不是面向对象的。 为了具有足够通用性,STL主要依赖于模板而不是封 装,继承和虚函数(多态性)——OOP的三个要素。 STL提供了大量的模板类和函数,可以在OOP和常规 编程中使用。所有的STL的大约50个算法都是完全通 用的,而且不依赖于任何特定的数据类型。
若按哈夫曼编码A:01010(5位)B:00(2位) C:0100(4位)D:111(3位)E:011(3位)F: 10(2位)G:01011(5位)H:110(3位),按 其出现频率则该电文的码长(即带权路径长度 WPL)为: 4*5+30*2+8*4+9*3+13*3+22*2+3*5+11*3 =270,
5.典型的几种数据结构:
顺序表,链表, 顺序栈,链栈,循环队列,链队列, 字符串, 多维数组, 二叉链表树,二叉顺序树, 邻接矩阵图,邻接表图, 散列表
4
设计题目
基于哈夫曼树的文件压缩/解压程序
(《数据结构课程设计案例精编》P329) 该参考教材同学们可自行借阅或在网上购买(一般8
折),也可联系出版社市场部集体定购(清华大学出版社 市场部电话:010-62788562(转分机号220)),可能有更 大折扣。
列出测试结果,包括输入的数据和相应的输出数 据。这里的测试数据应该完整和严格,最好多于需求 分析中所列的测试项。 7、 附录(电子版文档必须要,打印版该部分可不打印)
应附上带详细注释的可读性好的源程序。
数据结构课程设计报告-示例文档
13
时间安排
时间
内容
第1周
复习数据结构相关原理,明确题 目要求、确定数据结构与设计方 案
强调的是程序要做什么?明确规定:输入的形 式和输出、值的范围;输出的形式;程序所能 达到的功能;测试的数据:包括正确的输入和 错误的输入及其相应的输出结果; 2、 概要设计
问题解决的思路概述;说明程序中用到的 所有抽象数据类型的定义,主程序的流程以及 各程序模块之间的层次(调用)关系。
11
3、 详细设计 实现概要设计中定义所有数据类型,对每
明显的压缩了30位码长。
27
程序设计思路
文件由若干个字节组成,一个字节有 8bits,故有28=256种字节构成形式,对应 字节码为0-255。按此256种字节出现频率 可构造haffman树进行重新编码,编码后 每 字 节 的 新 编 码 平 均 长 度 将 <=8bits , 将 每个字节对应了压缩编码写到新文件中, 从而达到压缩文件的目的。
如何构造最优 树
前缀编码 16
一、最优树的定义
树的带权路径长度定义为:
树中所有叶子结点的带权路径长度之和
WPL(T) = wklk (对所有叶子结点)
例如:
在所有含 n 个叶子结点、并带相同权 值的 m 叉树中,必存在一棵其带权路径 长度取最小值的树,称为“最优树”。
17
75
9
24
WPL(T)= 72+52+23+ 43+92 =60
2
数据结构基本原理简要复习
1.数据元素之间的四种基本逻辑结构: 非线性,线性,树,图
2.数据物理表示(存储方法): 顺序存储,链式存储,索引存储,散列存储
3.算法的特征: 有穷性,可行性,确定性,正确性
算法设计的要求: 可读性,健壮性,高效性
3
4.算法的时间复杂度、空间复杂度及分析: O(1),O(n),O(logn),O(n2)
(2) 在 F 中选取其根结点的权值为最 小的两棵二叉树,分别作为左、 右子树构造一棵新的二叉树,并 置这棵新的二叉树根结点的权值 为其左、右子树根结点的权值之 和;
20
(3) 从F中删去这两棵树,同时加入 刚生成的新树;
(4)重复 (2) 和 (3) 两步,直至 F 中只 含一棵树为止。
21
例如: 已知权值 W={ 5, 6, 2, 9, 7 } 562 97
7
STL总结
尽管很多程序员仍然在使用标准C与C++函数,使用标 准C函数确实方便(如使用sprintf()进行格式化输出)。但 是C与C++函数不使用异常机制来报告错误,也不适合 处理新的数据类型。而且标准C与C++函数经常使用内 存分配技术,没有经验的程序员很容易写出bug来。. STL的最主要的两个特点:数据结构和算法的分离, 非面向对象本质。访问对象是通过象指针一样的迭代 器实现的;容器是象链表,矢量之类的数据结构,并 按模板方式提供;算法是函数模板,用于操作容器中 的数据。由于STL以模板为基础,所以能用于任何数 据类型和结构。
相关文档
最新文档