给定 n n n 个权值作为 n n n 个叶子构造一棵二叉树,使该树的带权路径长度达到最小,则这样的二叉树称为哈夫曼树(huffman tree),也称最优二叉树。
从根结点到某个叶子结点经过的边的数量称为该叶子结点的路径长度,每个叶子结点的路径长度与叶子结点的权值之积的和称为树的带权路径长度(Weighted Path Length of tree,简称WPL),计算公式如下:
W
P
L
=
∑
i
=
1
n
W
i
×
L
i
WPL=\sum _{i=1} ^{n}{W_i\times L_i}
WPL=i=1∑nWi×Li
其中,
n
n
n 为叶子结点个数,
W
i
W_i
Wi 为第
i
i
i 个叶子结点的权值,
L
i
L_i
Li 为第
i
i
i 个叶子结点路径长度。哈夫曼树就是
W
P
L
WPL
WPL 最小的二叉树。
哈夫曼树的构造的基本思想类似贪心,叫哈夫曼算法:权值越大的叶结点越靠近根节点,权值越小的叶结点越远离根结点。
构造方法:
假设有
n
n
n 个权值,则构造出的哈夫曼树有
n
n
n 个叶子结点。
n
n
n 个权值分别设为
w
1
、
w
2
、
…
、
w
n
w_1、w_2、…、w_n
w1、w2、…、wn ,则哈夫曼树的构造规则为:
- 将 w 1 、 w 2 、 … , w n w_1、w_2、…,w_n w1、w2、…,wn看成是有 n n n 棵树的森林(每棵树仅有一个结点);
- 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
- 从森林中删除选取的两棵树,并将新树加入森林;
- 重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。
哈弗曼树构造代码(非原创,改变自 @解除)
#include <bits/stdc++.h>
#define MinSize -1000
typedef struct HTNode *HuffmanTree;
struct HTNode{
int Weight;
HuffmanTree Left;
HuffmanTree Right;
};
typedef HuffmanTree ElementType;
typedef struct HeapNode *MinHeap;
struct HeapNode{
ElementType *Elements;
int Size;
int Capacity;
};
MinHeap Create(int N)
{
MinHeap H=(MinHeap)malloc(sizeof(struct HeapNode));
H->Elements=(ElementType*)malloc((N+1)*sizeof(ElementType));
H->Size=0;
H->Capacity=N;
HuffmanTree HT;
HT=(HuffmanTree)malloc(sizeof(struct HTNode));
HT->Weight=MinSize;
HT->Left=HT->Right=NULL;
H->Elements[0]=HT;
return H;
}
void Insert(MinHeap H,ElementType X)
{
if(H->Size==H->Capacity){
printf("最小堆已经满,不能插入!\n");
return;
}
int i=++H->Size;
for(;X->Weight<H->Elements[i/2]->Weight;i/=2){//这里没有等号会少运行一次
H->Elements[i]=H->Elements[i/2];
}
H->Elements[i]=X;
}
ElementType DeleteMin(MinHeap H)
{
if(H->Size==0){
printf("最小堆为空,无法删除!\n");
return NULL;
}
ElementType First=H->Elements[1],Last=H->Elements[H->Size];
int Weight=H->Elements[H->Size--]->Weight;
int Parent,Child;
for(Parent=1;2*Parent<=H->Size;Parent=Child){
Child=2*Parent;
if((Child!=H->Size)&&(H->Elements[Child+1]->Weight<H->Elements[Child]->Weight))
Child++;
if(Weight<=H->Elements[Child]->Weight) break;//注意,这里有等号可以少运行一次
else H->Elements[Parent]=H->Elements[Child];
}
H->Elements[Parent]=Last;
return First;
}
void PercDown(MinHeap H,int index)
{
ElementType i=H->Elements[index];
int Parent,Child;
for(Parent=index;2*Parent<=H->Size;Parent=Child){
Child=2*Parent;
if((Child!=H->Size)&&(H->Elements[Child+1]->Weight<H->Elements[Child]->Weight))
Child++;
if(i->Weight<=H->Elements[Child]->Weight) break;//注意,这里有等号可以少运行一次
else H->Elements[Parent]=H->Elements[Child];
}
H->Elements[Parent]=i;
}
void BuildMinHeap(MinHeap H)
{
int i=H->Size/2;
for(;i>0;i--)
PercDown(H,i);
}
HuffmanTree Huffman(MinHeap H)
{
int i,N=H->Size;
HuffmanTree T;
BuildMinHeap(H);
for(i=1;i<N;i++){
T=(HuffmanTree)malloc(sizeof(struct HTNode));
T->Left=DeleteMin(H);
T->Right=DeleteMin(H);
T->Weight=T->Left->Weight+T->Right->Weight;
Insert(H,T);
}
return DeleteMin(H);
}
void Preorder(HuffmanTree HT)
{
if(HT){
printf("%d ",HT->Weight);
Preorder(HT->Left);
Preorder(HT->Right);
}
}
int main()
{
MinHeap H=Create(50);
HuffmanTree HT;
for(int i=1;i<=5;i++){
HT=(HuffmanTree)malloc(sizeof(struct HTNode));
HT->Weight=6-i;//希望存入权重5,4,3,2,1
HT->Left=HT->Right=NULL;
H->Elements[i]=HT;
}
H->Size=5;
printf("没按权重调整的最小堆:");
for(int i=1;i<=H->Size;i++) printf("%d ",H->Elements[i]->Weight);
BuildMinHeap(H);
printf("\n按权重调整后的最小堆:");
for(int i=1;i<=H->Size;i++) printf("%d ",H->Elements[i]->Weight);
printf("\n测试DeleteMin:%d\n",DeleteMin(H)->Weight);
for(int i=1;i<=H->Size;i++) printf("%d ",H->Elements[i]->Weight);
printf("\n测试Insert:");
HT=(HuffmanTree)malloc(sizeof(struct HTNode));
HT->Weight=1;
HT->Left=HT->Right=NULL;
Insert(H,HT);
for(int i=1;i<=H->Size;i++) printf("%d ",H->Elements[i]->Weight);
printf("\n测试Huffman:");
HT=Huffman(H);
Preorder(HT);
return 0;
}