哈夫曼树编码解码

哈夫曼树编码:

1.权重获取:统计这段英文中数的频率以赋权,或直接读取文件中已经赋好的权重,或手动输入权重,在代码中手动输入权重(初始化)时会覆盖掉原本有权重的文件。

2.找两个最小的数:如果有三个或以上的数则取前两个,无所谓。

3.两数相加值替代两数成为新的权重:此时相加值的两个孩子为2步骤中找到的最小两数,左小右大

4.重复步骤2.3直到剩下一个权重root:此时二叉树已经建好了

5.编码:从字符(叶子结点)开始往上访问父节点(涉及到一开始建立的数据结构就需要有字符,父节点,左孩子,右孩子)再根据父节点判断左右孩子,左孩子赋值0,右孩子赋值1,直到访问到root结点,停下,将刚刚生成的字符串反过来即为该字符的编码

6.每一个字符对应一个编码,存在一个字符串数组中即可对应编码

译码:

1.从文件中读取所有的01字符串编码开始一个一个遍历

2.从根节点开始,遍历到0则访问左孩子,遍历到1则访问右孩子,每访问到一个结点都要判断这个结点中有没有字符的存在,如果有则输出该字符然后下一次遍历再从根节点开始,如果没有则再往下遍历。

3.输出所有的字符

代码:

//必做内容:
//一个完整的系统应具有以下功能:
//(1) I:初始化(Initialization)。建立霍夫曼树
// 从终端读入字符集大小n,以及n个字符和n个权值,建立霍夫曼树,并将它存于文件hfmTree中。
//(2) E:编码(Encoding)。
// 利用已建好的霍夫曼树(如不在内存,则从文件hfmTree中读入),
// 对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。
//(3) D:译码(Decoding)。
//利用已建好的霍夫曼树将文件CodeFile中的代码进行译码,结果存入文件Textfile中。
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
//哈夫曼树结点定义
struct huffmannode {
	char ch;//字符区域
	float weight;//字符对应权重区域
	int lchild, rchild, parent;
};
//编码对应的结构体
struct chcode {
	char c;
	string codec;
};
//哈夫曼树的类定义
class huffmantree {
	int num;//当前树一共有多少个叶子结点
	huffmannode* HT = NULL;
	char** HC;
	//生成编码
	void hfmcode();
public:
	huffmantree(char c[], float w[], int n);//字符数组 对应权重数组 字符个数
	//编码文件
	void encoding(string file,string file1);
	//译码文件
	void decoding(string file,string file1);
	void selectmin(int& s1, int& s2, int nn);
	~huffmantree();
};
// 建立哈夫曼树
// 找到最小的两个结点生成一个新的结点
// 遍历权重数组,先找到一个最小值存入结点替换数据为-1//标记为已使用
// 再找到另一个结点替换数据为这两个数据之和 字符则设为*
// 两个结点的parent指向新结点 新结点的lchild指向权重小的 rchild指向权重大的 
// 相同的则指向前面的
// 重复n-1次生成n-1个结点
// 最后一个结点指向root
//一共2n-1个结点 (用数组存?
huffmantree::huffmantree(char c[], float w[], int n) {
	num = n;
	//传入已经遍历完成的数组
	HT = new huffmannode[2 * n];
	
	for (int i = 1; i <= 2 * n - 1; i++) {
		HT[i].lchild = 0;
		HT[i].rchild = 0;
		HT[i].parent = 0;
	}
	//初始化加入
	for (int i = 1; i <= n; i++) {
		HT[i].ch = c[i];
		HT[i].weight = w[i];
		//cout << HT[i].ch << "," << HT[i].weight << endl;
	}
	int s1, s2;
	for (int i = n+1; i < 2 * n ; i++) {
		//cout << i << endl;
		selectmin(s1, s2, i-1);
		HT[i].lchild = s1; 
		HT[i].rchild = s2;
		HT[s1].parent = i;
		HT[s2].parent = i;
		HT[i].weight = HT[s1].weight + HT[s2].weight;
		//cout << HT[s1].weight << " " << HT[s2].weight << endl;
	}
	//生成编码写入hfmTree文件
	hfmcode();
}
void huffmantree::selectmin(int& s1, int& s2, int nn) {
	s1 = 0; s2 = 0;
	HT[0].weight = 10000;
	for (int i = 1; i <= nn; i++) {
		if (HT[i].parent != 0) continue;
		if (HT[i].weight < HT[s1].weight) {
			s2 = s1;
			s1 = i;
		}
		else if (HT[i].weight < HT[s2].weight ) s2 = i;
		else continue;
	}
	//cout << s1 << " " << s2 << endl;
	return;
}
void huffmantree::hfmcode() {
	char* code = new char[num];
	code[num-1] = '\0';
	HC = new char* [num+1];
	ofstream fout;
	fout.open("hfmTree.txt", ios::out);
	if (!fout.is_open()) {
		cout << "file could not open!" << endl;
		return;
	}
	for (int i = 1; i <= num; i++) {
		int start = num-1; int c = i; int f = HT[i].parent;
		while (f != 0) {
			start--;
			if (HT[f].lchild == c) code[start] = '0';
			else code[start] = '1';
			c = f; 
			f = HT[f].parent;
		}
		HC[i] = new char[num - start];
		strcpy(HC[i] ,&code[start]);
		fout << HT[i].ch << endl;
		fout << HC[i] << endl;
	}
	delete []code;
	//cout << "success!" << endl;
	fout.close();
}

huffmantree::~huffmantree() {
	delete[]HT;
}
//哈夫曼树的操作
// 编写单个字符的编码 在建立树后 生成每个字符的编码 存入字符串数组
// //左0右1
// 编写一个文件的编码 传入文件 对应字符进行编码写入文件
//hfmtree.encoding("ToBeTran.txt", "CodeFile");
void huffmantree::encoding(string file, string file1) {
	ifstream infile("hfmTree.txt");
	if (!infile.is_open()) {
		cout << "hfmTree.txt could not open!" << endl;
		return;
	}
	chcode* cd = new chcode[num];
	int i = 0;
	string line;
	while (getline(infile, line)) {
		cd[i].c = line[0];
		getline(infile, line);
		cd[i].codec = line;
		i++;
	}
	//用结构体数组把之前的编码存起来
	infile.close();
	
	ifstream fin;
	fin.open(file, ios::in);
	if (!fin.is_open()) {
		cout <<file<< " could not open!" << endl;
		return;
	}
	ofstream fout;
	fout.open(file1, ios::out);
	if(!fout.is_open()) {
		cout <<file1<< " could not open!" << endl;
		return;
	}
	while (getline(fin, line)) {
		for (int j = 0; j < line.length(); j++) {
			for (int k = 0; k < num; k++) {
				if (line[j] == cd[k].c) fout << cd[k].codec;
			}
		}
	}
	delete[]cd;
	fout.close();
	fin.close();
	cout << "succeed encoding!" << endl;
}
// 译码 判断哈夫曼树是否为空 传入文件 进行译码:根据01遍历哈夫曼树输出相应字符
void huffmantree::decoding(string file, string file1) {
	//ifstream infile("hfmTree.txt");
	//if (!infile.is_open()) {
	//	cout << "hfmTree.txt could not open!" << endl;
	//	return;
	//}
	//chcode* cd = new chcode[num];
	//int i = 0;
	string line;
	//while (getline(infile, line)) {
	//	cd[i].c = line[0];
	//	getline(infile, line);
	//	cd[i].codec = line;
	//	i++;
	//}
	用结构体数组把之前的编码存起来
	//infile.close();

	ifstream fin;
	fin.open(file, ios::in);
	if (!fin.is_open()) {
		cout << file << " could not open!" << endl;
		return;
	}
	ofstream fout;
	fout.open(file1, ios::out);
	if (!fout.is_open()) {
		cout << file1 << " could not open!" << endl;
		return;
	}
	int s = 2 * num - 1;
	while (getline(fin, line)) {
		for (int j = 0; j < line.length(); j++) {
			if (line[j] == '0') s = HT[s].lchild;
			else s = HT[s].rchild;
			if(s<=num){
				fout << HT[s].ch;
				s = 2 * num - 1;
			}
		}
	}
	fout.close();
	fin.close();
	cout << "succeed decoding!" << endl;
}
//菜单
void meau() {
	cout << "I------初始化" << endl
		<< "E------编码" << endl
		<< "D------译码" << endl
		<< "Q------退出" << endl;
	cout << "请输入功能:";
}
int main() {
	char q;
	while (true) {
		meau();
		cin >> q;
		if (q == 'Q') return 0;
//遍历hfm文件 存入两个数组
// //第一行采用 fin>>a   fin.get() 读一个换行符号
//第二行采用c=fin.get() 可以读空格符号 直到读到最后一个回车
//第二行循环a次读 fin>>数组[i]
		ifstream fin;
		fin.open("hfm.txt", ios::in);
		if (!fin.is_open()) {
			cout << "file could not open!" << endl;
			break;
		}
		int a;
		fin >> a;//字符个数
		fin.get();
		char* cha = new char[a+1];
		float* we = new float[a+1];
		for (int i = 1; i <= a; i++) {
			cha[i] = fin.get();
			fin.get();
		}
		fin.get();
		for (int i = 1; i <= a; i++) {
			fin >> we[i];
		}
		fin.close();
		/*cout << a << endl;
		for (int i = 0; i < a; i++) {
			cout << cha[i] << "," << we[i] << endl;
		}*/
		//读hfm文件存入数组中 先根据hfm文件建初始的哈夫曼树
		huffmantree hfmtree(cha, we, a);

		if (q == 'I') {
    //初始化(覆盖原来的hfm文件)文件写入操作
	//如果初始化了就要覆盖之前写入的文件
	//如果没有初始化就从原来的hfm文件中读入初始化
	//最终建立哈夫曼树都是要用hfm文件
	文件格式定义:一行字符加一行对应的权值 一行后回车也就是最后一个变量后面没有空格///读写空格符号?
	先读一行计算字符的数量 要建立对应的字符数组和权值数组
			//文件格式 第一行字符个数 第二行字符 第三行权值
			ofstream fout;
			fout.open("hfm.txt", ios::out);
			cout << "请输入字符集大小:";
			int a;
			while (true) {
				cin >> a;
				if (a > 0) break;
				cout << "字符集必须大于0,请重新输入:";
			}
			fout << a << endl;
			cout << "请输入" << a << "个字符(以空格间隔):";
			char A;
			for (int i = 1; i <= a; i++) {
				cin >> A;
				fout << A << ' ';
			}
			fout << endl;
			cout << "请输入" << a << "个字符对应的权值(以空格间隔):";
			float wei;
			for (int i = 1; i <= a; i++) {
				cin >> wei;
				fout << wei << ' ';
			}
			fout << endl;
			fout.close();
		}
		else if (q == 'E') {
	//编码(建立哈夫曼树) 文件读出操作 文件写入操作
	//读文件tobetran 进行编码 结果存入文件codefile中
	//输出一下字符串数组 编码文件 编码结果
			hfmtree.encoding("ToBeTran.txt", "CodeFile.txt");
		}
		else if (q == 'D') {
	//译码 文件读出操作 文件写入
	//哈夫曼树建好的情况下才能继续进行
	//读文件codefile 进行译码 结果存入文件textfile中
	//输出一下译码结果
			hfmtree.decoding("CodeFile.txt", "Textfile.txt");
		}
		else {
			cout << "无法此功能,请重新输入" << endl;	
		}
		system("pause");
		system("cls");//清空屏幕
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值