每日一题(9)——Huffman编码(贪心)

39 篇文章 5 订阅
3 篇文章 0 订阅

哈夫曼编码是典型的贪心问题,不断地通过局部最优,最终计算全局最优结果。

 

1951年,霍夫曼和他在MIT信息论的同学需要选择是完成学期报告还是期末考试。导师Robert M. Fano给他们的学期报告的题目是,查找最有效的二进制编码。由于无法证明哪个已有编码是最有效的,霍夫曼放弃对已有编码的研究,转向新的探索,最终发现了基于有序频率二叉树编码的想法,并很快证明了这个方法是最有效的。

由于这个算法,学生终于青出于蓝,超过了他那曾经和信息论创立者克劳德·香农共同研究过类似编码的导师。霍夫曼使用自底向上的方法构建二叉树,避免了次优算法Shannon-Fano编码的最大弊端──自顶向下构建树。

为每个符号建立一个叶子节点,并加上其相应的发生频率

  1. 当有一个以上的节点存在时,进行下列循环:
    1. 把这些节点作为带权值的二叉树的根节点,左右子树为空
    2. 选择两棵根结点权值最小的树作为左右子树构造一棵新的二叉树,且至新的二叉树的根结点的权值为其左右子树上根结点的权值之和。
    3. 把权值最小的两个根节点移除
    4. 将新的二叉树加入队列中.
  2. 最后剩下的节点暨为根节点,此时二叉树已经完成。

示例


Huffman Algorithm


符号 A B C D E
计数 15 7 6 6 5
概率 0.38461538 0.17948718 0.15384615 0.15384615 0.12820513

在这种情况下,D,E的最低频率和分配分别为0和1,分组结合概率的0.28205128。现在最低的一双是B和C,所以他们就分配0和1组合结合概率的0.33333333在一起。这使得BC和DE所以0和1的前面加上他们的代码和它们结合的概率最低。然后离开只是一个和BCDE,其中有前缀分别为0和1,然后结合。这使我们与一个单一的节点,我们的算法是完整的。


 

 

 

可得A代码的代码长度是1比特,其余字符是3比特。

字符 A B C D E
代码 0 100 101 110 111

    Entropy:


 

哈夫曼算法实现

//参考sophia_qing的priotity_queue做法
#include <iostream>#include <queue>
#include <vector>
#include <map>
#include <iterator>
#include <string>
using namespace std;

#define Nchar 8 //using 8 bit to describe all symbols
#define Nsymbols 1<<Nchar //can describe 256 symbols totally (include a-z, A-Z)

typedef vector<bool> Huff_code;
map<char, Huff_code> Huff_dic;

class Hnode 
{
public:
	char data;
	float fre;
	Hnode *left, *right;

	Hnode(){data='\0'; fre=0; left=NULL; right=NULL;}
	Hnode(char c, float f, Hnode* l, Hnode* r){data=c; fre=f; left=l; right=r;}
	~Hnode(){delete left; delete right;}

	bool isLeaf(){ return !left&&!right;}

};
//只能为类类型或枚举类型的操作数定义重载操作符;
//在把操作符声明为类的成员时,至少有一个类或枚举类型的参数按照值或者引用的方式传递;
//因此不能在class Hnode内直接重载;(prepare for pointer sorting);
class compare_node
{
public:
	bool operator () (Hnode* n1, Hnode* n2 )
	{
		return n1->fre > n2->fre;
	}
};

Hnode* bulidHtree(int *frequency)
{
	priority_queue<Hnode*, vector<Hnode*>, compare_node> Qtree;
	for(int i=0; i<Nsymbols; i++)
	{
		if(frequency[i])
			Qtree.push(new Hnode( (char) i, frequency[i], NULL, NULL));
	}

	while(Qtree.size()>1)
	{
		//不断定义Hnode* 
		Hnode* lc = Qtree.top();
		Qtree.pop();
		Hnode* rc =Qtree.top();
		Qtree.pop();

		Hnode* parents = new Hnode((char) 256, lc->fre + rc->fre, lc,rc);
		Qtree.push(parents);		//最后一次是把根节点push进Qtree中
	}
	
	return Qtree.top();		//最后返回的是Haffuman树的根节点
}

void Huffman_coding(Hnode* root, Huff_code& curcode)
{
	if (root->isLeaf())
	{
		Huff_dic[root->data]=curcode;
		return;
	}

	Huff_code lcode = curcode;		//定义左孩子的编码;
	Huff_code rcode = curcode;		//定义右孩子的编码;
	lcode.push_back(false);
	rcode.push_back(true);

	Huffman_coding(root->left, lcode);
	Huffman_coding(root->right, rcode);

}


int main()
{
	int freq[Nsymbols] = {0};
	char* c = "compress this is the string  into Huffman code";
	while(*c != '\0')
		freq[*c++]++ ;

	Hnode *root = bulidHtree(freq);
	Huff_code nullcode;
	nullcode.clear();
	Huffman_coding(root, nullcode);

	for(map<char, Huff_code>::iterator it = Huff_dic.begin(); it!=Huff_dic.end(); it++)
	{
		cout<<(*it).first<<'\t';
		copy(it->second.begin(), it->second.end(), ostream_iterator<bool>(cout));
		cout<<endl;
	}

}

 

 

Referance:

1. http://blog.csdn.net/abcjennifer/article/details/8020695

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值