Huffman的应用之文件压缩与解压缩汇总.docx
《Huffman的应用之文件压缩与解压缩汇总.docx》由会员分享,可在线阅读,更多相关《Huffman的应用之文件压缩与解压缩汇总.docx(22页珍藏版)》请在冰豆网上搜索。
![Huffman的应用之文件压缩与解压缩汇总.docx](https://file1.bdocx.com/fileroot1/2022-10/11/11cc8f07-ce8c-49dd-bd29-2bc463d2adad/11cc8f07-ce8c-49dd-bd29-2bc463d2adad1.gif)
Huffman的应用之文件压缩与解压缩汇总
Huffman的应用之文件压缩与解压缩
最近这段时间一直在学习树的这种数据结构,也接触到了Huffman树以及了解了什仫是Huffman编码,而我们常用的zip压缩也是利用的Huffman编码的特性,那仫是不是可以自己实现一个文件压缩呢?
当然可以了.在文件压缩中我实现了Huffman树和建堆Heap的代码,zip压缩的介绍>
下面开始介绍自己实现的文件压缩的思路和问题...
1).统计>读取一个文件统计这个文件中字符出现的次数.
2).建树>以字符出现的次数作为权值使用贪心算法构建Huffman树(根据Huffman树的特性>字符出现次数多的一定靠近根结点,出现次数少的一定远离根结点).
3).生成Huffman编码>规则左0右1.
4).压缩>再次读取文件,根据生成的Huffman编码压缩文件.
5).生成配置文件>将字符以及字符出现的次数写进配置文件中.
6).解压缩>利用配置文件还原出Huffman树,根据压缩文件还原出原文件.
7).测试>判断解压是否正确需要判断原文件和解压缩之后的文件是否相同,利用BeyondCompare软件进行对比.
下面是我举的一个简单的范例,模拟压缩和解压缩的过程,希望有读者有帮助
利用BeyondCompare软件进行对比>
在实现中出现了很多的问题,下面我提出几个容易犯的问题,仅供参考
1).在使用贪心算法构建Huffman树的时候,如果我们以unsignedchar一个字节来存储它总共有2^8=256个字符,如果将所有的字符都构建Huffman树,这不仅降低了效率还将分配极大的内存.所以我设立了非法值这个概念,只有当字符出现的次数不为0的时候才将该字符构建到Huffman树中.
2).在写压缩文件的时候我们是将字符对应的Huffman编码转化为对应的位,每当填满一个字节(8位)后再写入压缩文件中.如果最后一个字节没有填满我们就将已经填的位移位空出后几个位置,将未满的位置补0补满一个字节再写入压缩文件中.
3).如果我们将源文件压缩之后再去解压,因为你的Huffman树和Huffman编码都是在压缩函数中得到的,此时再去解压那仫你的Huffman编码以及不存在了该如何去还原文件呢?
这就是为什仫要生成配置文件的原因了,在配置文件中写入了字符以及字符出现的次数.在解压缩中根据配置文件构建新的Huffman树.
4).由压缩文件还原文件的时候如何知道压了多少个字符呢?
也就是说因为我们在压缩的时候最后一个字节是补了0的在解压缩的时候可能会把这个补的位当成字符的编码来处理.一种想法是在统计字符出现的次数的时候设置一个变量,每读取一个字符该变量就加1,最后将该变量写进配置文件中.另外一种想法就是根据根结点的权值,通过上述简单的实例观察可知根结点权值中的_count就是字符出现的次数.
解决了以上几个问题,我的程序已经可以压缩那256个字符并正确的还原了,那仫如果是大文件或者是汉字,图片以及音频视频呢?
1).因为有些特殊的字符编码,所以我们统计字符出现的次数的时候应该用的是unsignedchar,刚开始我用的文件结束标志是EOF在ASII中它的编码是-1此时已经不可以用EOF来判断是否文件结束了,所以我用了feof这个函数来判断文件是否结束.
2).统计字符出现次数应该用的类型是longlong,这就解决了大文件的压缩和解压缩的问题了.
3).因为汉字,图片,视频这些在内存中是以二进制的形式存在的,所以我们将以文本形式打开读或者写的修改为以二进制的形式读或者写.
为了验证大文件的压缩我找了一个8.09M的文件压缩之后是6.50M,并且可以正确还原.
1).测试效率>
2).利用BeyondCompare软件进行对比,如果一样说明压缩成功>
[cpp]viewplaincopyprint?
在CODE上查看代码片派生到我的代码片
#define_CRT_SECURE_NO_WARNINGS1
#pragmaonce
#include"Heap.h"
template
structHuffmanTreeNode
{
T_weight;
HuffmanTreeNode*_left;
HuffmanTreeNode*_right;
HuffmanTreeNode*_parent;
HuffmanTreeNode(constT&w=T())
:
_weight(w)
_left(NULL)
_right(NULL)
_parent(NULL)
{}
};
template
classHuffmanTree
{
typedefHuffmanTreeNodeNode;
public:
HuffmanTree()
:
_root(NULL)
{}
HuffmanTree(constT*a,size_tsize)
:
_root(NULL)
{
_root=_CreatHuffmanTree(a,size);
}
//将未出现的字符过滤出来,不构造堆
HuffmanTree(constT*a,size_tsize,constT&invalid)
{
_root=_CreatHuffmanTree(a,size,invalid);
}
Node*GetRoot()
{
return_root;
}
~HuffmanTree()
{
_Destroy(_root);
}
protected:
Node*_CreatHuffmanTree(constT*a,size_tsize)
{
structNodeLess
{
booloperator()(Node*l,Node*r)const
{
returnl->_weight_weight;
}
};
HeapminHeap;
//建立结点并放入vector中
for(size_ti=0;i{
Node*tmp=newNode(a[i]);
minHeap.Push(tmp);
}
//取出较小的两个结点作为左右孩子并构建父结点
while(minHeap.Size()>1)
{
Node*left=minHeap.Top();
minHeap.Pop();
Node*right=minHeap.Top();
minHeap.Pop();
Node*parent=newNode(left->_weight+right->_weight);
parent->_left=left;
parent->_right=right;
left->_parent=parent;
right->_parent=parent;
minHeap.Push(parent);
}
returnminHeap.Top();
}
//思路类似不带过滤功能的
Node*_CreatHuffmanTree(constT*a,size_tsize,constT&invalid)
{
structNodeLess
{
booloperator()(Node*l,Node*r)const
{
returnl->_weight_weight;
}
};
HeapminHeap;
//建立结点并放入vector中
for(size_ti=0;i{
if(a[i]!
=invalid)
{
Node*tmp=newNode(a[i]);
minHeap.Push(tmp);
}
}
//取出较小的两个结点作为左右孩子并构建父结点
while(minHeap.Size()>1)
{
Node*left=minHeap.Top();
minHeap.Pop();
Node*right=minHeap.Top();
minHeap.Pop();
Node*parent=newNode(left->_weight+right->_weight);
parent->_left=left;
parent->_right=right;
left->_parent=parent;
right->_parent=parent;
minHeap.Push(parent);
}
returnminHeap.Top();
}
void_Destroy(Node*&root)
{
if(root==NULL)
return;
Node*cur=root;
if(cur)
{
_Destroy(cur->_left);
_Destroy(cur->_right);
deletecur;
cur=NULL;
return;
}
}
protected:
Node*_root;
};
voidtestHuffmanTree()
{
inta[]={0,1,2,3,4,5,6,7,8,9};
intsize=sizeof(a)/sizeof(a[0]);
HuffmanTreeht(a,size);
}
[cpp]viewplaincopyprint?
在CODE上查看代码片派生到我的代码片
#define_CRT_SECURE_NO_WARNINGS1
#pragmaonce
//利用仿函数的特性实现代码的复用性
template
structSmall
{
booloperator()(constT&l,constT&r)
{
returnl}
};
template
structLarge
{
booloperator()(constT&l,constT&r)
{
returnl>r;
}
};
template>//缺省是建小堆
classHeap
{
public:
Heap()
{}
Heap(constT*a,intsize)
{
assert(a);
_a.reserve(size);
for(inti=0;i{
_a.push_back(a[i]);
}
//建堆的时候从倒数第一个非叶子结点开始.
for(intj=(size-2)/2;j>=0;--j)
{
_AdjustDown(