算法导论16.3_Huffman编码+优先队列(堆实现) + 贪心

 

由于每次寻找权值最小的节点去构建一颗树,所以 Huffman 编码也是 贪心算法 的 一个例子。

 

哈夫曼算法原理

 

1952年, David A. Huffman提出了一个不同的算法,这个算法可以为任何的可能性提供出一个理想的树。香农-范诺编码(Shanno-Fano)是从树的根节点到叶子节点所进行的的编码,哈夫曼编码算法却是从相反的方向,暨从叶子节点到根节点的方向编码的。

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

 

示例

 

Huffman Algorithm

 

符号ABCDE
计数157665
概率0.384615380.179487180.153846150.153846150.12820513

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

 

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

字符ABCDE
代码0100101110111

 

    Entropy:

#include <iostream>
#include <cstring>
#include <cstdlib>

using namespace std;

#define MAXN 100000
struct HNode
{
	char data;
	double weight;
	int left;
	int right;
	int parent;
	HNode():left(-1),right(-1),parent(-1){}
};


struct Num
{
	int num;
	double weight;
};

class Huffman_Tree
{
public:
	Huffman_Tree(int num):n(num){
		int i;

		m = 2*n-1;
		input(num);
		
		//对堆进行初始化
		Heapify(num);
		for(i=1; i<=num; i++)
		{
			A[i].num = i;
			A[i].weight = T[i].weight;
		}
		Build_Min_Heap(A);
	}

	void input(int n){
		for(int i=1; i<=n; i++)
		{
			cin>>T[i].data>>T[i].weight;
		}
	}

	void Create_Haffman_Tree();
	void Unicode();
	void Print(int l);
	
	//最小优先队列的实现代码
	void Heapify(int n){heap_size = n;}
	int parent(int i){return i/2;}
	int left(int i){return 2*i;}
	int right(int i){return 2*i+1;}
	void Min_Heapify(Num *A, int i);
	void Build_Min_Heap(Num *A);
	int Heap_Minimum(Num *A);
	int Heap_Extract_Min(Num *A);
	void Heap_Decrease_Key(Num *A, int i, double key);
	void Min_Heap_Insert(Num *A, int n, double key);
private:
	//Huffman树
	HNode T[MAXN+1];
	int n;			//记录叶子结点的个数
	int m;			//计算Haffman_Tree树树中结点的总数
	
	//最小优先队列
	Num A[MAXN+1];
	int heap_size;	//堆中结点的个数
};

#define INF 100.0

void Huffman_Tree::Create_Haffman_Tree()
{
	int min1,min2;
	int j;
	for(j=n+1; j<=m; j++)
	{
		min1 = Heap_Extract_Min(A);
		min2 = Heap_Extract_Min(A);
		T[min1].parent = j;
		T[min2].parent = j;
		T[j].right = min2;
		T[j].left = min1;
		T[j].weight = T[min1].weight+T[min2].weight;
		Min_Heap_Insert(A,j,T[j].weight);
	}
	for(int i=1; i<=m; i++)
		cout<<T[i].data<<" "<<T[i].weight<<endl;
}


void Huffman_Tree::Unicode()
{
	int j,k,l;

	cout<<"哈夫曼编码开始"<<endl;
	for(j=1; j<=n; j++)
	{
		cout<<T[j].data<<"      ";
		l = j;
		Print(l);
		cout<<endl;
	}
}

void Huffman_Tree::Print(int l)
{
	int k;
	if(l == m)
		return;
	k = T[l].parent;
	if(l == T[k].left)
	{
		Print(k);
		cout<<0;
	}
	if(l == T[k].right)
	{
		Print(k);
		cout<<1;
	}
}

//对哈夫曼树中最小优先队列的操作
void Huffman_Tree::Min_Heapify(Num *A, int i)
{
	int l,r;
	int minimum;
	Num temp;

	l = left(i);
	r = right(i);
	if(l<=heap_size && A[l].weight<A[i].weight)
	{
		minimum = l;
	}
	else
		minimum = i;

	if(r<=heap_size && A[r].weight<A[minimum].weight)
		minimum = r;

	if(minimum!=i)
	{
		temp = A[i];
		A[i] = A[minimum];
		A[minimum] = temp;
		Min_Heapify(A,minimum);
	}
}

void Huffman_Tree::Build_Min_Heap(Num *A)
{
	int i;

	for(i=heap_size/2; i>=1; --i)
	{
		Min_Heapify(A,i);
	}
}

int Huffman_Tree::Heap_Minimum(Num *A)
{
	return A[1].num;
}

int Huffman_Tree::Heap_Extract_Min(Num *A)
{
	int min;
	if(heap_size<1)
		cerr<<"heap underflow"<<endl;
	min = A[1].num;                                                             
	A[1] = A[heap_size];
	heap_size--;
	Min_Heapify(A,1);

	return min;
}

void Huffman_Tree::Heap_Decrease_Key(Num *A, int i, double key)
{
	Num temp;

	if(key>A[i].weight)
		cerr<<"new key is bigger than the current key"<<endl;
	A[i].weight = key;
	while(i>1 && A[parent(i)].weight>A[i].weight)
	{
		temp = A[i];
		A[i] = A[parent(i)];
		A[parent(i)] = temp;
		i = parent(i);
	}
}

void Huffman_Tree::Min_Heap_Insert(Num *A, int n,double key)
{
	heap_size++;
	A[heap_size].weight = INF;
	A[heap_size].num = n;
	Heap_Decrease_Key(A,heap_size,key);
}

#define UNSUBMIT
int main()
{
#ifdef UNSUBMIT
	freopen("data.in","r",stdin);
#endif
	int n;

	cin>>n;
	Huffman_Tree *T = new Huffman_Tree(n);
	T->Create_Haffman_Tree();
	T->Unicode();
	
	return 0;
}

 

//测试数据

8
a 0.07
b 0.19
c 0.02
d 0.06
e 0.32
f 0.03
g 0.21
h 0.10

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值