您的位置:首页 > 其它

哈夫曼编码/译码

2015-12-21 08:20 232 查看
大家应该还记得谍战剧中经常出现的一个词”密码本“。在消息传递时,往往不会明文发送,而要以另一种加密的形式发送内容。假如现在要发送这样一条信息"lisan is a turncoat"。大家很容易知道内容是"李三是叛徒"。那么这样的一条消息是不会直接这样发送的,往往加密以后,也就是通过密码本,可能这段内容加密后的内容是这样的"111001010010101000001110",当把这样的消息传送出去,敌方就很难知道这段编码的意思是什么,除非得到了你的密码本,这也就是密码如此重要的原因。

好,现在用哈夫曼思想来进行编码和译码,实现我们自己的密码本。(哈夫曼思想这里不做介绍)

为了简化内容,我们假设密码本只有27个字符,第一个是空格,随后是ABCD...........XYZ 26个字母。

如下代码实现的功能是:1.根据27个字符所对应的权值,进行编码,***密码本。

2.随后模拟传递消息,对发送过来的一条加过密的消息(也就是一大堆01010)进行译码,转换为明文。

3.最后,将一条明文进行加密

部分代码代码参考严蔚敏的数据结构,如下代码在VisualStudio2015调试编译通过,对如下代码开头第一句 #define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1不理解请参考 点击打开链接



一:main部分

#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1

#include<iostream>
#include<string>//use string
#include<string.h>//use strcpy
using namespace std;

typedef struct 
{
	char ch;
	int weight;
	int parent, lChild, rChild;
}HTNode, * HuffmanTree;

typedef char** HuffmanCode;

void Select(HuffmanTree & HT, int end, int & s1, int & s2);
void HuffmanCoding(HuffmanTree & HT, HuffmanCode & HC, int * w, char *ch, int n);
void HuffmanDecoding(HuffmanTree &HT, string text, int n);

int main()
{
	
	int n;//字符集大小
	int *w;//字符对应的权值
	char *code;//n个字符

	// ABCDEFGHIJKLMNOPQRSTUVWXYZ
	//186 64 13 22 32 103 21 15 47 57 1 5 32 20 57 63 15 1 48 51 80 23 8 18 1 16 1
	//THIS PROGRAM IS MY F***ORITE

	
	//1.***密码本
	cout << "请输入字符集大小: ";
	cin >> n;
	
	w = new int
;//申请内存
	code = new char[n+1];

	cout << "请输入" << n << "个字符: ";
	getchar();//注意这里
	for (int i = 0; i < n; i++)
		cin.get(code[i]);//可以读取空格
	code
 = '\0';

	cout << "请输入字符对应的" << n << "个权值: ";
	for (int i = 0; i < n; i++)
		cin >> w[i];	
	
	HuffmanTree myTree;
	HuffmanCode myCode;
	HuffmanCoding(myTree, myCode, w, code, n);

	cout << "\n\n-----------------------------------------------------------------------\n";
	cout << "密码本为: \n";
	for (int i = 0; i < n; i++)
	    cout << code[i]<<"-----"<<myCode[i] << endl;
	
	
	//2.根据明文,进行加密编码
	cout << "\n\n-----------------------------------------------------------------------\n";
	cout << "请输入明文进行加密编码: ";
	getchar();
	string str;
	getline(cin,str);

	cout << "编码为: ";
	for (int i = 0; i < str.length(); i++)
	{
		if (str[i] == ' ')
			cout << myCode[0];	
		else
			cout << myCode[str[i] - 'A' + 1];		
	}

	//3.根据一段编码,进行译码,输出明文
	cout << "\n\n-----------------------------------------------------------------------\n";
	cout << "请输入编码进行解密译码: ";
	string text;
	cin >> text;
	cout << "译码后明文是: ";
	HuffmanDecoding(myTree, text, n);

	cout << endl;

	return 0;
}


二:具体实现

1.在HT[0...end]选择parent为0且weight最小的两个节点,其序号分别为是s1,s2(s1是最小weight节点的序号)

void Select(HuffmanTree & HT, int end, int & s1, int & s2)
{
	int tag1, tag2;
	tag1 = tag2 = 999999;//分别对应是s1,s2

	for (int i = 0; i <= end; i++)
	{
		if (HT[i].parent == -1)
		{
			if (HT[i].weight < tag1)
			{
				tag1=HT[i].weight;
				s1 = i;
			}
			else if (HT[i].weight < tag2)
			{
				tag2=HT[i].weight;
				s2 = i;
			}
		}
	}
}


2.w存放n个字符的权值(均大于0),构造哈夫曼树HT,并求出n个字符的哈夫曼编码HC,ch数组存放n个字符。

void HuffmanCoding(HuffmanTree & HT, HuffmanCode & HC, int * w, char * ch, int n)
{
	if (n <= 1)
		return;

	int m = 2 * n - 1;
	HT = new HTNode[m];

	HuffmanTree p;
	int i;
	for (p = HT, i = 0; i < n; i++, p++, w++ , ch++)//权值依次对应放入哈夫曼树
		*p = { *ch,*w,-1,-1,-1};

	for (; i < m; i++, p++)
		*p = { 0,-1,-1,-1,-1 };

	int s1, s2;
	for (i = n ; i < m; i++)
	{
		Select(HT, i - 1, s1, s2);

		HT[s1].parent = i; HT[s2].parent = i;
		HT[i].lChild = s1; HT[i].rChild = s2;
		HT[i].weight = HT[s1].weight + HT[s2].weight;
	}

	//从叶子到根逆向求每个字符的哈夫曼编码
	int start;
	HC = new char*
;
	char * cd = new char
;
	cd[n-1] = '\0';

	for (int i = 0; i < n; i++)
	{
		start = n - 1;

		for (int c = i, f = HT[i].parent; f != -1; c = f, f = HT[f].parent)
		{
			if (HT[f].lChild == c)
				cd[--start] = '0';
			else
				cd[--start] = '1';
		}
		
		HC[i] = new char[n - start];
		strcpy(HC[i], &cd[start]);
	}

	delete[]cd;
}


3.根据加密的电文,译成字符明文

void HuffmanDecoding(HuffmanTree &HT, string text,int n)
{
	int len = text.length();
	int j = 2 * n - 2;

	for (int i = 0; i < len; i++)
	{
		if (text[i] == '0')
			j = HT[j].lChild;//走向左孩子
		else
			j = HT[j].rChild;//走向右孩子

		if (HT[j].lChild == -1)//如果是叶节点
		{
			cout << HT[j].ch;
			j = 2 * n - 2;//回到根节点
		}
	}
}


只要把上述代码放在一起就可以运行,下图是运行的结果,因为图片较大,分为两张图片。



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: