【C语言】哈夫曼树的创建、编码、译码

根据哈夫曼编码的原理,编写一个程序,在用户输入结点权值的基础上求赫夫曼编码,并能把给定的编码进行译码。

(1)初始化:从键盘输入一字符串(或读入一文件),统计出现的字符和每个字符出现的频率,将字符出现的频率作为结点的权值,建立哈夫曼树。对各个字符进行哈夫曼编码,最后打印输出字符及每个字符对应的哈夫曼编码。

(2)编码:利用已建好的哈夫曼树对“输入串”进行哈夫曼编码,最后打印输入串对应的哈夫曼编码(写入文件)。

(3)译码:利用已建好的哈夫曼树对给定的一串代码进行译码,并打印输出得到的字符串。(选作)

测试数据:对字符串{casbcatbsatbat}进行编码;对电文“1101000”译码。字符集D={   ?},出现频率为w={?}

#define _CRT_SECURE_NO_WARNINGS  1
#pragma warning(disable:6031)
#include<bits/stdc++.h>
using namespace std;
typedef struct TreeNode
{
	char str;//字母
	int Leistuing;//权值
	int lchrild, rchrild, parent;//左孩子,右孩子,双亲
}TreeNode, Tree;
void InitTreeNode(TreeNode& T)//对树的节点进行初始化
{
	T.lchrild = 0;//左孩子初始化
	T.rchrild = 0;//右孩子初始化
	T.parent = 0;//双亲初始化
	T.str = '0';//字符
	T.Leistuing = 0;//权值初始化为0
}
void Select(Tree* T, int TLength, int& S1, int& S2)//选择树中权值最小且双亲为0的两个子树
{
	//寻找第一个双亲域为0且权值最小的结点
	int min = 0;
	//找到第一个双亲域为0的,下标暂存到min
	for (int i = 0; i < TLength; ++i)
	{
		if (T[i].parent == 0) {
			min = i;
			break;
		}
	}

	for (int i = 0; i <TLength; i++)
		if (T[i].parent == 0 && T[min].Leistuing > T[i].Leistuing)
			min = i;
	S1 = min;

	for (int i =0; i < TLength; ++i)
	{
		if (T[i].parent == 0 && i != S1) {
			min = i;
			break;
		}
	}

	for (int i = 1; i<TLength; i++)
		if (T[i].parent == 0 && T[min].Leistuing > T[i].Leistuing && i != S1)
			min = i;
	S2 = min;
}
bool CreatHT(Tree* T, int &TLength)//哈夫曼树的建立
{
	int num = TLength;//为进行循环的次数
	for (int i = 1; i < num; i++)//便利所有节点,建立哈夫曼树
	{
		int S1, S2;
		Select(T, TLength, S1, S2);
		T[TLength].Leistuing = T[S1].Leistuing + T[S2].Leistuing;//对新节点的权值进行赋值
		T[S1].parent = TLength;
		T[S2].parent = TLength;
		T[TLength].lchrild = S1;
		T[TLength].rchrild = S2;
		TLength++;//为第一个空节点下标
	}
	return true;
}
bool decoding(char numcode[], Tree T[], char code[20][20], int Lcode[], int Tlength)
{
	int temp = 0 ,bools=0;
	while (numcode[temp] != '\0')
	{
		for (int i = 0; i < (Tlength-1)/2+1; i++)
		{
			for (int j = 0; j < Lcode[i]; j++)
			{
				if (numcode[temp] == code[i][j])
				{
					bools++;
					temp++;
				}
				else
				{
					bools = 0;
					temp = temp -j;
					break;
				}
			}
			if (bools)
			{
				
				cout << T[i].str;
			}
		}
	}
	return true;
}
int main()
{
	Tree T[50];
	for (int i = 0; i < 50; i++)
	{
		InitTreeNode(T[i]);
	}
	char str[20] = { '\0' };
	cin >> str;
	int  Length = 0, TLength = 0, NumOf[26];//Length用于记录临时存储于str的字符串的长度,TLength用于存储树中新建子树个数,NumOf用于存储数字化的26个英文字母出现次数
	for (int i = 0; i < 26; i++)
	{
		NumOf[i] = 0;
	}
	while (str[Length] != '\0')
	{
		NumOf[str[Length] - 97]++;//通过ASCII值转化,将字母转化为int型以记录其权值
		Length++;
	}
	for (int i = 0; i < 26; i++)
	{
		if (NumOf[i] != 0)
		{
			char j = 'a' + i;
			T[TLength].str = j;
			T[TLength].Leistuing = NumOf[i];
			TLength++;
		}
	}
	CreatHT(T, TLength);
	int Lcode[20] = {0};
	char code[20][20] = { '\0' };
	for (int i = 0; i <= (TLength - 1) / 2; i++)
	{
		int  G = i;
		while (T[G].parent != 0)//当未到根节点时
		{
			if (T[T[G].parent].lchrild == G){//判断是否为其双亲的左子树
				code[i][Lcode[i]] = '0';
				Lcode[i]++;
			}
			if (T[T[G].parent].rchrild == G){//判断是否为其双亲的右子树
				code[i][Lcode[i]] = '1';
				Lcode[i]++;
			}
			G = T[G].parent;//往上找节点
		}
	}
	cout << "字符" << " " << "权" << " " << "双亲" << " " << "左孩子" << " " << "右孩子"<<"\n";
	for (int i = 0; i < TLength; i++)
	{
		cout << T[i].str << " " << T[i].Leistuing << " " << T[i].parent << " " << T[i].lchrild << " " << T[i].rchrild << "\n";
	}
	for (int i = 0; i <=(TLength-1)/2; i++)//因为原编码为反着的的,改程序用于对编码倒置
	{
		for (int j = 0; j <Lcode[i]/2; j++)
		{
			char temp;
			temp = code[i][j];
			code[i][j] = code[i][Lcode[i]-1 - j];
			code[i][Lcode[i]-1- j] = temp;
		}
	}
	cout << "编码为:\n";
	for (int i = 0; i<(TLength-1)/2+1  ;i++)
	{
		cout << T[i].str << " ";
		for (int j = 0; j < 20; j++)
		{
			cout << code[i][j];
		}
		cout << "\n";

	}
	char numcode[20] = { '\0' };
	cout << "请输入要翻译的编码\n";
	cin >> numcode;
	cout << "译码为:\n";
	decoding(numcode, T, code, Lcode, TLength);
	cout << "字符集D的出现频率为W={";
	for (int i = 0; i < (TLength - 1) / 2 + 1; i++)
	{
		cout << T[i].str << ":" << T[i].Leistuing << ",";
	}
	cout << "}";
	return 0;
}

运行结果如图:

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值