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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM