根据哈夫曼编码的原理,编写一个程序,在用户输入结点权值的基础上求赫夫曼编码,并能把给定的编码进行译码。
(1)初始化:从键盘输入一字符串(或读入一文件),统计出现的字符和每个字符出现的频率,将字符出现的频率作为结点的权值,建立哈夫曼树。对各个字符进行哈夫曼编码,最后打印输出字符及每个字符对应的哈夫曼编码。
(2)编码:利用已建好的哈夫曼树对“输入串”进行哈夫曼编码,最后打印输入串对应的哈夫曼编码(写入文件)。
(3)译码:利用已建好的哈夫曼树对给定的一串代码进行译码,并打印输出得到的字符串。(选作)
测试数据:对字符串{casbcatbsatbat}进行编码;对电文“1101000”译码。字符集D={ ?},出现频率为w={?}
用c++的类来完成,对于输入的字符串,我们要知道树中每个字符出现的频率,还有每个字符对应的编码,这中一一对应关系,我们用map完成。我们还需要记录哈夫曼树的根,便于对电文译码。
对于二叉树的构建,我们不用数组,就用二叉链表解决,每个节点对应的成员为字符、频率、左右孩子。
//节点类
class HuffmanNode{
public:
char date;
int freq;
HuffmanNode *left;
HuffmanNode *right;
HuffmanNode(char date,int freq)
{
this->date=date;
this->freq=freq;
left=right=NULL;
}
};
private:
map freq; //字符--对应频次
map codes; //字符--对应编码
HuffmanNode *root;
先统计字符的种类和每种字符出现个数,随后以每一种字符构建一个节点,作为哈夫曼树的叶子,随后将这些节点都放进一个数据结构L中,当L中还有一个以上的节点时,找出权值(也就是频率)最小的A,和次最小的B。A,B的权值相加构成一个新节点C,C的左孩子为A,右孩子为B。随后C进入L,A、B从L中出来。当L中只剩下一个节点时,剩下的那个就是哈夫曼树的根。
现在的难点就是找出这么一种数据结构L,能随时拿出两个数据或加入一个数据,而且每次拿出的两个数据要是权值最小的,也就是权值最小的优先拿出。
课本上用线性表完成,利用叶子节点和二度节点的数量关系创建一个数组,每次找数组中没有父亲的权值最小的节点最小,这个找的过程很麻烦。
既然是权值最小的优先拿出,而c++stl库就有一种数据结构,能按照自己设定的优先级来处理对象---优先队列。我们只需要将权值小的放在堆顶,每次拿出堆顶的两个出来就行,就避免了找的这个过程。
Huffman(string s)
{//用构造函数来构造一颗哈夫曼数
//创建 字符---出现次数 的键值对
for(int i=0;i,Compare> pq;
for(auto it:freq)
{
// HuffmanNode *p=new HuffmanNode(it.first,it.second);
pq.push(new HuffmanNode(it.first,it.second));
}
//开始,合并最小的两个,出队,合并结果入队的循环操作,直到队里只剩下一个。
while(pq.size()>1)
{
//最小值为左儿子,次小值为右儿子
HuffmanNode* left=pq.top();
pq.pop();
HuffmanNode* right=pq.top();
pq.pop();
HuffmanNode* parent=new HuffmanNode('#',left->freq+right->freq);
parent->left=left;
parent->right=right;
pq.push(parent);
}
root=pq.top();//最后剩下根
getcodes(root,"");
}
//求出每个叶子对应的01编码
void getcodes(HuffmanNode* root,string s)
{
if(root==NULL)
{
return ;
}
if(root->date!='#')
{
codes[root->date]=s;
}
//往左走
getcodes(root->left,s+'0');
getcodes(root->right,s+'1');
}
在步骤一里
//刚开始输入串对应的哈夫曼编码
string getEncodedText(string s)
{
string Encodedtext="";
for(auto i:s)
{
Encodedtext+=codes[i];
}
return Encodedtext;
}
//构建好哈夫曼树后,任意输入一个有效串(串中每个字符在哈夫曼树的叶子中能找到),输出01编码
string str_to_01(string s)
{
string codeed="";
for(int i=0;ileft;
else
cur=cur->right;
if(cur==NULL)
{
cout<<"你输入的01编码无法翻译!!!!!\n";
return "######";
}
if(cur->date!='#')
{
ans+=cur->date;
cur=root;
}
}
if(cur!=root) cout<<"所给的01编码不完整,但是能翻译。\n";
return ans;
}
#include
#include