基本思想:
压缩:
1、统计出文件中相同字符出现的次数
2、获取哈夫曼编码
次数作为权值构建哈夫曼树
3、重新编码,写回压缩文件
保存头文件:
源文件后缀
编码信息的行数
每个字符的权
保存编码
解压缩:
1、获取原文件后缀
2、获取每个字符出现的次数,即权值
3、利用之前后的的权值,还原哈夫曼树
4、找到对应的叶子节点,将信息保存到解压文件中
在写压缩文件之前,首先需要实现堆和哈夫曼树
1,建堆
#include<iostream>
#include<vector>
using namespace std;
//利用仿函数的特性实现代码的复用性
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)
{
adjust_down(j);
}
}
void Push(const T& x)
{
_a.push_back(x);
adjust_up(_a.size() - 1);
}
void Pop()
{
assert(!_a.empty());
swap(_a[0], _a[_a.size() - 1]);
_a.pop_back();
adjust_down(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;
}
void adjust_down(int root)
{
Compare com;
int parent = root;
int child = parent * 2 + 1;//parent的左孩子
while (child < _a.size())
{
/*if rightchild > leftchild,child->right
while 里面我们已经可以确定child(左孩子下标一定小于size
但是我们不能保证右孩子的下标小于size,所以if语句里我们
要判断一下,以免访问越界)
*/
if (child + 1<_a.size() && com(_a[child + 1], _a[child]))
//if (child + 1<_a.size() && _a[child + 1] > _a[child])
{
++child;
}
if (com(_a[child], _a[parent]))//如果是>则为大堆
//if (_a[child] > _a[parent])//if child>parent,swap
{
swap(_a[child], _a[parent]);
parent = child; //让parent指向child,继续向下调整
child = child * 2 + 1;
}
else
break;
}
}
void adjust_up(int child)
{
Compare com;
size_t parent = (child - 1) >> 1;