数据结构实验2:哈夫曼树应用

数据结构实验2

题目要求
实现一个哈夫曼编码系统,系统包括以下功能:
(1) 字符信息统计:读取待编码的源文件SourceFile.txt,统计出现的字符及其频率。
(2) 建立哈夫曼树:根据统计结果建立哈夫曼树以及哈夫曼码表,将各字符对应的编码表保存在文件Code.txt中。
(3) 对源文件进行编码:根据哈夫曼码表,将SourceFile.txt中的字符转换成相应的编码文件ResultFile.txt。
(4) 选做内容:
完成译码功能:对任意一个给定的由01组成的文件,根据哈夫曼码表翻译成由字符组成的源文件。


分析
这是一个即实用又有趣的题目,但是设计到很多的知识点,非常耗时。若用c++实现最好有c++ STL 使用基础。
我将程序分成几个主要部分:

  1. 统计文章的字符出现频率。
  2. 构造哈夫曼树并生成哈夫曼编码。
  3. 将文章按照生成的哈弗曼编码加密成只含有0和1的问。
  4. 保存编码到文件,读取编码并还原哈夫曼树。
  5. 读取经过特定编码加密的文件,并又这个编码树还原成可读的内容。

简单实现哈夫曼方法请看:我的哈夫曼算法

知识点
【文件读写 】
如读取原文,保存编码, 难点是将数据序列化和反序列化。
【c++ map】
用来保存各个字符出现的次数,若不会使用,将难以下手或代码量倍增。
【c++ primary_queue】
用于构造哈夫曼树,能极大提高效率和缩短代码长度

笔记
第一次写时用char来保存字符,但是后来发现原文中若出现中文,char类型没法保存,最后溢出了。于是用整数来保存
字符,输出时简单的类型转换即可。


? 以下代码实现题目基本要求和选做内容

MYCODE

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

const char *sourcefile = "D://SourceFile.txt";    //原文
const char *mapfile = "D://mapfile.txt";		//存储字典的文件
const char *encryptfile = "D://entryfile.txt";	//存储原文加密后形成的文本

ofstream mapf;

map<int , int> iimap;		//保存字符在原文中出现的字典,用int来保存字符
map<int , string> ccmap;	//保存字符对应hufuman编码的字典


//哈夫曼树节点结构
struct node{
	int val;		//字符
	int  times;		//出现次数
	string code;	//哈夫曼编码
	node* left;
	node* right;
	node(int c, int t, node* l, node* r){
		val = c;
		times = t;
		right = r;
		left = l;
	}
	bool operator < (const node &n){
		return this->times > n.times;
	}
};

struct cmp{
	bool operator()(const node* a, const node* b){
		return a->times > b->times;
	}
};

//保存节点的优先队列,times小的节点优先输出
priority_queue<node*, vector<node*>, cmp> pq;

//读取原文并且统计每个字符出现的次数
void Read_and_static(){
	ifstream fout;
	string buf;
	fout.open(sourcefile);
	while (getline(fout, buf)){
		iimap['\n']++;			//getline后换行符不会出现在buf中,getline一次则说明有一个换行符
		for (int i = 0; i < buf.length(); i++){
			cout << buf[i];
			iimap[ buf[i] ]++;
		}	
	}
	fout.close();
}

//将iimap的数据放到优先队列pq中,需要先统计原文得到iimap。
void init_queue(){
	for (auto i : iimap){
		node *tn = new node(i.first, i.second, NULL, NULL);
		pq.push(tn);
	}
}

//生成哈夫曼树,返回头结点。
node* build_huffmantree1(){
	init_queue();
	while (pq.size()>1){
		node* t1 = pq.top();
		pq.pop();
		node* t2 = pq.top();
		pq.pop();
		node* newnode = new node(-1,t1->times + t2->times, t1, t2);
		pq.push(newnode);
	}
	node* head = pq.top();
	return head;	
}

//显示 pq 的内容,即各个字符以及其出现的次数
void display(){
	priority_queue<node*, vector<node*>, cmp> temp = pq;
	while (temp.empty() == false){
		node* t = temp.top();
		printf("%c ---- %d \n", t->val, t->times);
		temp.pop();
	}
	return;
}


//从头结点开始,递归遍历哈夫曼树,从而为节点设置哈夫曼编码,
void set_code(node* n){
	if (n->left == NULL && n->right == NULL){
		cout << char(n->val) << "   " << n->code << endl;
		return;
	}
	if (n->left != NULL){
		n->left->code += n->code + "0";
		set_code(n->left);
	}
	if (n->right != NULL){
		n->right->code += n->code + "1";
		set_code(n->right);
	}
	return;
}

//递归遍历一个哈弗曼树,将编码字典保存到文件和ccmap中
void Save_code(node *n){
	if (n->left == NULL && n->right == NULL){	
		cout << n->val << "  -->  " << n->code << endl;
		ccmap[n->val] = n->code;
		mapf << n->val << endl << n->code << endl;
		return;
	}
	if (n->left != NULL)  Save_code(n->left);
	if (n->right != NULL) Save_code(n->right);
}

//按照特定格式读取件中保存的哈弗曼字典,返回一个map表示字符对应的哈夫曼编码
map<int, string> read_codefile(){
	ifstream code;
	code.open(mapfile);
	int t = 0;
	string tkey,val;
	map<int, string>tcode;
	while (getline(code, tkey) && getline(code,val)){
		int key = atoi(tkey.c_str());
		tcode[key] = val;
	}
	code.close();
	return tcode;
}

//更具指定的字典构造一个哈夫曼树,返回头节点
node* build_huffmantree2(map<int,string> code){
	init_queue();
	node *head = new node(-1, 0, NULL, NULL);
	for (auto i : code){
		string code = i.second;
		int val = i.first;
		node *p = head;
		for (int i = 0; i < code.length(); i++){
			if (code[i] == '0'){ 
				if (p->left == NULL){
					p->left = new node(-1, 0, NULL, NULL);
				}
				p = p->left;
			}
			else{
				if (p->right == NULL){
					p->right = new node(-1, 0, NULL, NULL);
				}
				p = p->right;
			}
		}
		p->code = code;
		p->val = val;
	}
	return head;
}

//将文本加密为哈夫曼编码
void encryption(){
	ifstream from(sourcefile);
	ofstream to(encryptfile);
	string temp;
	while (getline(from, temp)){
		if (temp.length() == 0) to << ccmap['\n'];	
		for (int i = 0; i < temp.length(); i++){
			to << ccmap[temp[i]];
		}
	}
	from.close();
	to.close();
	return;
}

//将哈夫曼编码还原为原文,前提条件是ccmap已经按照相应的字典初始化
void decode(node *head){
	ifstream from(encryptfile);
	char tc;
	node *p = head;
	while (from.get(tc)){
		if (tc == '0')p = p->left;
		else p = p->right;
		if (p->left == NULL && p->right == NULL){
			cout << char(p->val);
			p = head;
		}
	}
	return;
}

int main(){
	bool create = false;
	if (create){ //加密源文件(读取源文件,生成编码字典和一个加密后的文件)
		Read_and_static();					//得到了字符——出现次数 字典 iimap
		node *head = build_huffmantree1();	//又iimap构建一棵树,但是树的节点未设置编码
		set_code(head);						//为哈夫曼树的节点附上编码
		mapf.open(mapfile);
		Save_code(head);					//将编码保存到文件中
		mapf.close();
		encryption();						//将原文按照字典解析成编码,放到文件中,相当于加密
	}
	else{		//解密源文件(读取加密文件和编码字典,还原并输出原文)
		ccmap = read_codefile();		//读取编码字典
		node *head = build_huffmantree2(ccmap);		//构造树
		set_code(head);					//设置编码
		
		decode(head);
	}
	return 0;
}

能力有限,若发现不知之处欢迎留言

你可能感兴趣的:(数据结构与算法,趣味编程)