哈夫曼树(Huffman)及其无损压缩实现
大家好!过完新年后,在宿舍闲着蛋疼,就把上学期无法实现的哈夫曼树的压缩及其解压实现一下了。至于怎么压缩各种格式的文件,现在还没有找到实现方法。
以下是代码实现:
//Huffman_H.h
#ifndef Huffman_H
#define Huffman_H
#include <string>
#include <fstream>
#include <queue>
#include <vector>
using namespace std;
const int MAX_SIZE = 256; //含有256个ACSII码
struct huffman_node
{
char id;
int freq;
string code;
huffman_node* left;
huffman_node* right;
huffman_node* parent;
//默认的构造函数
huffman_node(char ch = '\0',string s = "", int cou = 0,huffman_node* lef = NULL, huffman_node* rig = NULL,huffman_node* par = NULL):
id(ch),code(s),freq(cou),left(lef),right(rig),parent(par){}
};
typedef huffman_node* node_ptr;
class Huffman
{
protected:
node_ptr node_array[MAX_SIZE];
fstream in_file;
fstream out_file;
ofstream decompress_file; //解压使用的文件输入输出流
node_ptr child;
node_ptr parent;
char id;
int decodingnum; //记录解码时有多少种不同的字符
string in_file_name;
string out_file_name;
class compare
{
public:
bool operator()(const node_ptr& c1,const node_ptr& c2) const
{
return (*c1).freq > (*c2).freq;
}
};
//用于比较优先队列中元素间的顺序
priority_queue<node_ptr,vector<node_ptr>,compare > pq;
//根据输入文件构造包含字符及其频率的数组
void create_node_array();
public:
node_ptr root;
//根据输入和输出流初始化对象
Huffman(string s1,string s2);
//构造优先队列
void create_pq();
//构造Huffman树
void create_huffman_tree();
//计算Huffman编码
void calculate_huffman_codes(node_ptr & root);
//将Huffman编码和编码后的文本存入文件
void save_to_file();
//将压缩好的文件解压回去
void decompress_to_file();
};
#endif
//Huffman.cpp
#include "Huffman_H.h"
#include <iostream>
#include <fstream>
void Huffman::create_node_array()
{
unsigned char ch; //定义读取的字节单位
int num = 0;
in_file.open(in_file_name,ios::in | ios::binary); //使用二进制读入文件即可对所有的文件格式进行操作
in_file.read((char *)&ch,sizeof(char)); //一个字节一个字节的读入,并存储相应的字符极其权重
while(!in_file.eof())
{
num = int(ch);
if(node_array[num]->freq == 0)
{
node_array[num]->id = ch;
}
node_array[num]->freq++;
in_file.read( (char *)&ch, sizeof(char));
}
in_file.close();
//将字符极其权重存入优先队列中进行排序
}
//构造函数,主要负责初始化工作
Huffman::Huffman(string s1,string s2)
{
in_file_name = s1;
out_file_name = s2;
root = NULL;
parent = NULL;
child = NULL;
decodingnum = 0;
int i;
//这里值得注意,因为要给256个哈夫曼树的结点划分空间
for(i = 0;i < MAX_SIZE;i++)
{
node_array[i] = new huffman_node();
}
}
//构造优先队列
void Huffman::create_pq()
{
create_node_array();
int i;
for(i = 0; i < MAX_SIZE;i++)
{
if(node_array[i]->freq > 0)
{
decodingnum++;
pq.push(node_array[i]);
}
}
}
//构造Huffman树
void Huffman::create_huffman_tree()
{
while(pq.size() > 1) ////在队列中的数在一个以上时进行节点合并构建Huffman树
{
node_ptr h1,h2;
h1 = pq.top();
pq.pop();
h2 = pq.top();
pq.pop();
node_ptr h = new huffman_node();
h->freq = h1->freq + h2->freq;
h->left = h1;
h->right = h2;
h1->parent = h;
h2->parent = h;
pq.push(h); //按照huffman算法,再把它放回优先队列里面
}
root = pq.top();
root->parent = NULL;
}
//计算Huffman编码
void Huffman::calculate_huffman_codes(node_ptr & root)
{
//若哈夫曼树为空,则不操作.
if(root == NULL)
;
else
{
if(root->left != NULL)
{
root->left->code = root->code + '0';
calculate_huffman_codes(root->left);
}
if(root->right != NULL)
{
root->right->code = root->code + '1';
calculate_huffman_codes(root->right);
}
}
}
//将Huffman编码和编码后的文本存入文件
void Huffman::save_to_file()
{
int bytenum = 0; //记录字节的位数,每8位刷新一次
unsigned char temp,ch = 0;
int i;
out_file.open(out_file_name,ios::out|ios::binary); //将编码的情况写入压缩文件里
out_file.write((char*)&decodingnum,sizeof(char)); //将编码情况先存入压缩文件中以便在解压时使用,有多少种不同类型的字符
for(i = 0;i < MAX_SIZE;i++)
{
if(node_array[i]->freq != 0)
{
//将字符和频率逐个字符读入
out_file.write((char *)&node_array[i]->id,sizeof(char));
out_file.write((char *)&node_array[i]->freq,sizeof(char));
}
}
in_file.open(in_file_name,ios::in|ios::binary);
in_file.read((char*)&temp,sizeof(char));
while(!in_file.eof())
{
for(i = 0;i < node_array[temp]->code.size();i++)
{
ch <<= 1; //将读到的字符左移一位
bytenum++;
if(node_array[temp]->code[i] == '1')
ch = ch | 1;
if ( bytenum == 8 ) //当字节位移满八次以后进行一次压缩
{
out_file.write( (char *)&ch, sizeof(char) );
bytenum = 0;
ch = 0;
}
}
in_file.read((char*)&temp,sizeof(char));
}
//如果压缩的字节不足8位,则用0补足
if(bytenum > 0 && bytenum < 8)
{
ch <<= (8 - bytenum);
out_file.write((char*)&ch,sizeof(char));
}
in_file.close();
out_file.close();
}
//将压缩好的文件解压回去
void Huffman::decompress_to_file()
{
int temp,num,i,totalcount;
unsigned char ch;
decompress_file.open(out_file_name);
out_file.open(in_file_name,ios::in|ios::binary);
out_file.read((char*)&ch,sizeof(char));
decodingnum = int(ch); //表头所记录的不同类型的字符数目
num = decodingnum;
cout << num << endl;
cout << out_file_name <<endl;
out_file.read((char*)&ch,sizeof(char));
while(num) ////从新的压缩文件中读取编码情况
{
temp = (int)ch;
node_array[temp]->id = ch;
out_file.read((char*)&ch,sizeof(char));
node_array[temp]->freq = int(ch);
pq.push(node_array[temp]);
out_file.read((char*)&ch,sizeof(char));
num--;
}
create_huffman_tree();
totalcount = root->freq;
while(!out_file.eof())
{
for(i = 7;i >= 0;i--)
{
temp = (ch >> i)&1; //每次提出最开头的一位,这样表达的好处是ch并没改变其值
if(root->left == NULL && root->right == NULL) //当到达哈夫曼树的叶子时
{
decompress_file << root->id;
root = pq.top(); //返回哈夫曼树顶部
decodingnum--;
if(!totalcount)
return;
}
//当为"0",向左子树迭代
if(temp == 0)
{
root = root->left;
}
//当为"1",向右子树迭代
else
{
root = root->right;
}
}
out_file.read((char*)&ch,sizeof(char));
}
out_file.close();
decompress_file.close();
}
//test.cpp
#include <iostream>
#include <string>
#include <fstream>
#include "Huffman_H.h"
using namespace std;
/*
int main(int argc,char *argv[])
{
if(argc != 3)
{
cout << "Usage:\n\t huffmancoding inputfile outputfile" << endl;
exit(1);
}
Huffman h(argv[1],argv[2]);
h.create_pq();
h.create_huffman_tree();
h.calculate_huffman_codes(h.root);
h.save_to_file();
cout << endl;
return 0;
}
*/
/*压缩情况
int main()
{
string s1,s2;
cin >> s1;
cin >> s2;
Huffman h(s1,s2);
h.create_pq();
h.create_huffman_tree();
h.calculate_huffman_codes(h.root);
h.save_to_file();
cout << "Done" << endl;
return 0;
}
*/
//
int main()
{
string s1,s2;
cin >> s1;
cin >> s2;
Huffman h(s1,s2);
h.decompress_to_file();
cout << "Done!" << endl;
return 0;
}
//

浙公网安备 33010602011771号