HuffmanTree的创建、编码、解码操作

#include <iostream>
#include <cstdlib>
#include <cstring>
#pragma warning(disable:4996) //忽略strcpy的错误
using namespace std;

typedef struct HTNode {
	char c; //存储的字符
	int weight; //权值
	int parent, lchild, rchild; //双亲序号 左孩子和右孩子序号
}HTNode, * HuffmanTree;
typedef char** HuffmanCode; //把HuffmanCode定义为指向char的指针的指针 相当于一个二维数组 列存放每个节点 行存放每个结点的字符

int min(HuffmanTree HT, int k) { //求出权值最小的一个结点 
	int i = 0;
	int minNum, minNum_weight; //分别存放权值最小的结点序号和对应的权值

	while (HT[i].parent != 0) i++;  //找到第一个权值为0的结点

	minNum = i; minNum_weight = HT[i].weight;

	for (; i <= k; i++) {
		if (HT[i].parent == 0 && HT[i].weight < minNum_weight) {
			minNum = i;
			minNum_weight = HT[i].weight;
		}
	} //for

	HT[minNum].parent = 1; //找出最小权值的结点之后 将其parent域置为1 不需要知道确切的parent值 做个标记以便区分即可
	return minNum;
}

void Select(HuffmanTree HT, int k, int& s1, int& s2) {  //求出权值最小的两个结点 调用min()函数两次
	s1 = min(HT, k);
	s2 = min(HT, k);
}

HuffmanTree CreatHuffmanTree(int n) {  //创建HuffmanTree
	int s1, s2;
	int m = 2 * n - 1; //n代表有多少个根节点要合并 m代表合并完总结点的个数 
	if (n <= 1) return 0;
	HuffmanTree HT = new HTNode[m + 1]; //0号不用 多分配一个存储单元
	for (int i = 1; i <= m; i++) { //初始化操作
		HT[i].lchild = 0;
		HT[i].rchild = 0;
		HT[i].parent = 0;
	}
	for (int i = 1; i <= n; i++) { //赋值操作,输入每个结点的字符以及权值
		cin >> HT[i].c >> HT[i].weight;
	}
	//以上为初始化操作  接下来生成HuffmanTree
	for (int i = n + 1; i <= m; i++) {
		Select(HT, i - 1, s1, s2); //在HT[k](1<=k<=i-1)中找到两个权值最小的且parent值为0的结点,将这两个结点在数组中的序号值用s1 s2返回
		HT[s1].parent = i; HT[s2].parent = i; //从数组中删除s1 s2,利用对其parent赋值的方法
		HT[i].lchild = s1; HT[i].rchild = s2;  //对新节点的左右孩子赋值
		HT[i].weight = HT[s1].weight + HT[s2].weight; //新结点的权值等于左右孩子之和
	}
	return HT;
}

void HTPrint(HuffmanTree HT, int n) { //打印HuffmanTree
	cout << "weight" << "\t\t" << "parent" << "\t\t" << "lchild" << "\t\t" << "rchild" << endl;
	for (int i = 1; i <= 2 * n - 1; i++)
		cout << HT[i].weight << "\t\t" << HT[i].parent << "\t\t" << HT[i].lchild << "\t\t" << HT[i].rchild << endl;
}

void CreatHuffCode(HuffmanTree HT, HuffmanCode& HC, int n) {  //从叶子逆方向回溯到根节点求出原始结点的编码
	int location; //code字符数组的下标 每次循环从最后一位开始
	int f, c;
	char* code; //临时存放编码的字符数组
	HC = new char* [n + 1]; //n个结点分配n+1个头指针
	code = new char[n];
	code[n - 1] = '\0'; //字符数组编码结束符
	for (int i = 1; i <= n; i++) {  //从1->n 逐个字符求编码
		location = n - 1; c = i; f = HT[i].parent;
		while (f != 0) {
			--location;
			if (HT[f].lchild == c) code[location] = '0';
			else				   code[location] = '1';
			c = f; f = HT[f].parent; //继续执行 类似于链表的-》next
		} //while
		HC[i] = new char[n - location]; //为二维数组的行分配空间
		strcpy(HC[i], &code[location]); //注意strcpy的用法 里面是地址 HC[i]本身就是地址 对于code[]需要取地址&, 而且code[location]可以从有编码的位置开始赋值 前面的空都不会复制过去
	} //for

}

void HCPrint(HuffmanTree HT, HuffmanCode HC, int n) { //打印HuffmanTree中每个原始结点的编码 
	cout << "字符对应的编码为:" << endl;
	for (int i = 1; i <= n; i++) {
		cout << "字符" << HT[i].c << "的编码为:" << HC[i] << endl;
	}
}

void Encode(HuffmanTree HT, HuffmanCode HC, char str[], int n, int cnt) { //对输入的字符进行编码操作 注意:输入的字符仅限于上述HuffmanTree中的原始字符
	for (int i = 0; i < cnt; i++) {
		for (int j = 1; j <= n; j++) { //每个都比较一遍
			if (str[i] == HT[j].c) {
				cout << HC[j];
				break;
			}
		}
	}
}

void DeCode(HuffmanTree	HT, char charCode[], int n) { //解码操作 利用对编码后的字符从头到尾 在对应的HuffmanTree中深度搜索的方式得到解码字符
	int intCode[50];
	for (int i = 0; i < strlen(charCode); i++) {
		if (charCode[i] == '0') intCode[i] = 0;
		else				    intCode[i] = 1; //对整型数组进行赋值初始化
	}
	int cnt = 0; //计数已经解码过的个数
	while (cnt < strlen(charCode)) {
		int j = 2 * n - 1;
		while (HT[j].lchild != 0 && HT[j].rchild != 0) {
			if (intCode[cnt] == 0) j = HT[j].lchild;
			if (intCode[cnt] == 1) j = HT[j].rchild;
			cnt++; //一次循环结束 计数加一
		}
		cout << HT[j].c;
	}
}

int main() {
	int n; char Receivechar[20];
	char charCode[50]; //接受要解码的01字符串
	HuffmanCode HC;
	cout << "请输入原根结点数n:";
	cin >> n;

	HuffmanTree HT = CreatHuffmanTree(n); //创建HuffmanTree 
	CreatHuffCode(HT, HC, n); //生成每个原始结点的HuffmanCode 

	HTPrint(HT, n); //打印HuffmanTree 
	HCPrint(HT, HC, n); //打印每个原始结点的HuffmanCode 

	cout << "请输入要编码的字符:" << endl;
	int i = 0;
	while ((Receivechar[i] = getchar()) != '#') i++; //i表示多少个字符
	cout << "编码为:";
	Encode(HT, HC, Receivechar, n, i); //输出编码 
	cout << endl;

	getchar(); //用于接收上一步输入的回车符 防止下面的gets_s接收到这个回车符从而被跳过!!!
	cout << "请输入要解码的01串:";
	gets_s(charCode); //获取字符串
	cout << "解码为:";
	DeCode(HT, charCode, n);
}

 2021-11-17补充:

以上没有文件操作,如果想把字符的编码和解码内容保存到文件中,可以利用几句文件的操作。

例如:

#include <iostream>
#include <fstream>
using namespace std;

int main() {
	ofstream  fout;
	fout.open("C:/Users/xxx/Desktop/xxx.txt",ios::out);
	
	int n1 = 100;
	char name[] = "xxx";
	fout<<n1<<endl;
	fout<<name<<endl;
	
	fout.close();
} 

文件还是要好好学,数据多的时候还能靠控制台输入吗。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值