由于每次寻找权值最小的节点去构建一颗树,所以 Huffman 编码也是 贪心算法 的 一个例子。
哈夫曼算法原理
1952年, David A. Huffman提出了一个不同的算法,这个算法可以为任何的可能性提供出一个理想的树。香农-范诺编码(Shanno-Fano)是从树的根节点到叶子节点所进行的的编码,哈夫曼编码算法却是从相反的方向,暨从叶子节点到根节点的方向编码的。
- 为每个符号建立一个叶子节点,并加上其相应的发生频率
- 当有一个以上的节点存在时,进行下列循环:
- 把这些节点作为带权值的二叉树的根节点,左右子树为空
- 选择两棵根结点权值最小的树作为左右子树构造一棵新的二叉树,且至新的二叉树的根结点的权值为其左右子树上根结点的权值之和。
- 把权值最小的两个根节点移除
- 将新的二叉树加入队列中.
- 最后剩下的节点暨为根节点,此时二叉树已经完成。
示例
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:
#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