哈夫曼编码

题目要求:

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

(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<char,int> freq;      //字符--对应频次 
		map<char,string> 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<s.size();i++)
			{
				freq[s[i]]++;
			}
			//先创建叶子 
			//将没找到暂时父亲的节点放在优先队列里,将(节点的,不单单只叶子)频次大小设定为优先级 
			priority_queue<HuffmanNode*,vector<HuffmanNode*>,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');
		}

步骤二、求出每个叶子的01编码

在步骤一里

步骤三、利用已经键好的哈夫曼树,对输入串解码或对01串译码

//刚开始输入串对应的哈夫曼编码 
		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;i<s.length();i++)
			{
				if(freq.find(s[i])==freq.end())
				{
					cout<<"字符"<<s[i]<<"无法编码!!!!!!\n";
					return "######";
				}
				codeed+=codes[s[i]];
			}
			return codeed;
		}
		//输入01编码,输出解码后的串(解码) 
		string _01_to_str(string encode)
		{
			string ans="";
			HuffmanNode *cur=root;
			for(auto i:encode)
			{
				if(i=='0')
				    cur=cur->left;
				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<iostream>
#include<map>
#include<queue>
#include<vector>
#include<string>
using namespace std;
//节点类 
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;
		}
};
//确定优先队列的优先级 
class Compare{
	public:
		bool operator()(HuffmanNode *a,HuffmanNode *b)
		{
			return a->freq > b->freq;
		}
};

class Huffman{
	public:
		Huffman(string s)
		{//用构造函数来构造一颗哈夫曼数 
			//创建  字符---出现次数  的键值对 
			for(int i=0;i<s.size();i++)
			{
				freq[s[i]]++;
			}
			//先创建叶子 
			//将没找到暂时父亲的节点放在优先队列里,将(节点的,不单单只叶子)频次大小设定为优先级 
			priority_queue<HuffmanNode*,vector<HuffmanNode*>,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;i<s.length();i++)
			{
				if(freq.find(s[i])==freq.end())
				{
					cout<<"字符"<<s[i]<<"无法编码!!!!!!\n";
					return "######";
				}
				codeed+=codes[s[i]];
			}
			return codeed;
		}
		//输入01编码,输出解码后的串(解码) 
		string _01_to_str(string encode)
		{
			string ans="";
			HuffmanNode *cur=root;
			for(auto i:encode)
			{
				if(i=='0')
				    cur=cur->left;
				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;
		}
		void showfreq()
		{
			cout<<"字符--对应频率\n";
			for(auto i:freq)
			{
				cout<<i.first<<"   "<<i.second<<endl;
			}
		}
		void showcodes()
		{
			cout<<"字符--对应编码\n";
			for(auto i:codes)
			    cout<<i.first<<"    "<<i.second<<endl;
		}
	private:
		map<char,int> freq;      //字符--对应频次 
		map<char,string> codes;  //字符--对应编码 
		HuffmanNode *root;
};
int main()
{
	string s="casbcatbsatbat";
	string _01code="1101000";
	Huffman h(s);
    
    cout<<s<<endl;
    h.showfreq();
    h.showcodes();
    
    cout<<"对原字符串{"<<s<<"}编码:";
    string encodedText = h.getEncodedText(s);
    cout<<encodedText<<endl;
    
    cout<<"对{"<<_01code<<"}解码:";
    string decoded = h._01_to_str(_01code);
    cout<<decoded<<endl;
    
    cout<<"对"<<"{abcstdd}编码\n";
    cout<<h.str_to_01("abcstdd")<<endl;
	return 0;
}

运行:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值