题目描述
1、问题描述
给定n个字符及其对应的权值,构造Huffman树,并进行huffman编码和译(解)码。
构造Huffman树时,要求左子树根的权值小于、等于右子树根的权值。
进行Huffman编码时,假定Huffman树的左分支上编码为‘0’,右分支上编码为‘1’。
2、算法
构造Huffman树算法:
⑴根据给定的n个权值(w1, w2, …, wn)构成n棵二叉树的集合F={T1, T2, …, Tn},其中每棵二叉树Ti中只有一个权值为wi的根结点。
⑵在F中选取两棵根结点的权值最小的树,作为左、右子树构造一棵新的二叉树,且置其根结点的权值为其左、右子树权值之和。
⑶在F中删除这两棵树,同时将新得到的二叉树加入F中。
(4)重复⑵,⑶,直到F只含一棵树为止。
3、Huffman编码算法:
⑴从Huffman树的每一个叶子结点开始。
⑵依次沿结点到根的路径,判断该结点是父亲结点的左孩子还是右孩子,如果是左孩子则得到编码‘0’,否则得到编码‘1’,先得到的编码放在后面。
⑶直到到达根结点,编码序列即为该叶子结点对应的Huffman编码。
4、Huffman译(解)码算法:
⑴指针指向Huffman树的根结点,取第一个Huffman码。
⑵如果Huffman码为‘0’,将指针指向当前结点的左子树的根结点;如果Huffman码为‘1’,将指针指向当前结点的右子树的根结点。
⑶如果指针指向的当前结点为叶子结点,则输出叶子结点对应的字符;否则,取下一个Huffman码,并返回⑵。
⑷如果Huffman码序列未结束,则返回⑴继续译码。
输入
第一行测试次数
第2行:第一组测试数据的字符个数n,后跟n个字符
第3行:第一组测试数据的字符权重
待编码的字符串s1
编码串s2
其它组测试数据类推
输出
第一行~第n行,第一组测试数据各字符编码值
第n+1行,串s1的编码值
第n+2行,串s2的解码值,若解码不成功,输出error!
其它组测试数据类推
输入样例1
2
5 A B C D E
15 4 4 3 2
ABDEC
00000101100
4 A B C D
7 5 2 4
ABAD
1110110
输出样例1
A :1
B :010
C :011
D :001
E :000
1010001000011
error!
A :0
B :10
C :110
D :111
0100111
DAC
NOTICE:详细解释见注释。
#include <iostream>
#include <string>
using namespace std;
class TNode
{
public:
char data;
int weight;
int parent;
int lchild;
int rchild;
string code;
};
class HuffmanTree
{
private:
TNode* tree;
int lnum;
int nodenum;
public:
HuffmanTree()
{
cin >> lnum;
nodenum = 2 * lnum - 1;
tree = new TNode[nodenum];
for (int i = 0; i < lnum; i++)
{
cin >> tree[i].data;
}
for (int i = 0; i < lnum; i++)
{
cin >> tree[i].weight;
}
for (int i = 0; i < lnum; i++)
{
tree[i].parent = -1;
tree[i].lchild = -1;
tree[i].rchild = -1;
}
}
void CreateHuffman()
{
int n = lnum;
while (n != nodenum)
{
//寻找最小权值的两个结点
int min1 = 9999, min2 = 9999;
int index1 = 0, index2 = 0;
for (int i = 0; i < n; i++)
{
if (tree[i].parent == -1 && tree[i].weight < min1)
{
min2 = min1;
index2 = index1;
min1 = tree[i].weight;
index1 = i;
}
else if (tree[i].parent == -1 && tree[i].weight < min2)
{
min2 = tree[i].weight;
index2 = i;
}
}
//将两个结点合并
tree[index1].parent = n;
tree[index2].parent = n;
tree[n].weight = tree[index1].weight + tree[index2].weight;
tree[n].lchild = index1;
tree[n].rchild = index2;
tree[n].parent = -1;
n++;
}
}
void HuffmanCode()
{
for (int i = 0; i < lnum; i++)
{
int parent = tree[i].parent;
int pre = i;//记录先前的结点
while (parent != -1)
{
if (tree[parent].lchild == pre)
{
tree[i].code = "0" + tree[i].code;
}
else
{
tree[i].code = "1" + tree[i].code;
}
pre = parent;
parent = tree[parent].parent;
}
}
for (int i = 0; i < lnum; i++)
{
cout << tree[i].data << " :" << tree[i].code << endl;
}
}
void Code(string s)
{
string res;
for (int i = 0; i < s.size(); i++)
{
for (int j = 0; j < lnum; j++)
{
if (s[i] == tree[j].data)
{
res = res + tree[j].code;
break;
}
}
}
cout << res << endl;
}
void Decode(string s)
{
string res;
int i = 0;
int k = nodenum - 1;
while (i != s.size())
{
if (s[i] == '0')
{
k = tree[k].lchild;
}
else
{
k = tree[k].rchild;
}
i++;
if (tree[k].lchild == -1 && tree[k].rchild == -1)
{
res = res + tree[k].data;
k = nodenum - 1;
}
}
if (k == nodenum - 1)//说明能解出最后一个字符
{
cout << res << endl;
}
else
{
cout << "error!" << endl;
}
}
};
int main()
{
int t;
cin >> t;
while (t--)
{
HuffmanTree hft;
hft.CreateHuffman();
hft.HuffmanCode();
string s;
cin >> s;
hft.Code(s);
cin >> s;
hft.Decode(s);
}
return 0;
}