哈夫曼树(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;
}
//

 

posted @ 2013-02-20 23:19  中大黑熊  阅读(4463)  评论(1编辑  收藏  举报