给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。
树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。
如下图所示,权值分别为1 2 3 4 的叶子节点,可以构造出4颗形状不同的二叉树,(下图仅列出两个),他们的带权路径长度分别为:
图1、 WPL = 1 * 2 + 2 * 2 + 3 * 2 + 4 * 2 = 20
图2、 WPL = 1 * 3 + 2 * 3 + 3 * 2 + 4 * 1 = 19
我们把最小的带权路径长度的二叉树称作哈夫曼树,或 最优二叉树, 图2 所示的二叉树是一棵哈夫曼树。
建树的步骤:
1、 给定对应的n 个权值,是对应节点构成n棵 二叉树的森林T, 每一个二叉树Ti 中都只有一个带权值为Wi 的根节点,其左右子树均为空。
2、 选取两个节点权值最小的子树分别作为左右子树构造一棵新的二叉树,且置新的二叉树的根节点的权值为器左右子树上根节点的权值之和
3、 在森林T 中,用新得到的二叉树代替选取的两棵树。
4、 重复2. 3 ,直到T 只喊一个树为止,得到的便是哈夫曼树。
下面里一个建树的代码,可以手动模拟一下,便于理解:
typedef struct node
{
int weight;
int parent;
int lson;
int rson;
}Htnode;
void creatHT(Htnode ht[], int n)
{
for(int i = 0; i < 2 * n; i ++)
{
ht[i].parent = ht[i].lson = ht[i].rson = -1;
}
for(int i = n; i < 2 * n - 1; i ++)
{
int min1 = min2 = 9999;
int lnode = rnode = -1;
for(int j = 0; j < i; j ++)
{
if(ht[i].parent == -1)
{
if(ht[j].weight < min1)
{
min2 = min1;
rnode = lnode;
min1 = ht[j].weight;
rnode = j;
}
else if(ht[j].weight < min2)
{
min2 = ht[j].weight;
rnode = j;
}
}
ht[i].weight = ht[lnode].weight + ht[rnode].weight;
ht[i].lson = lnode;
ht[i].rson = rnode;
ht[lnode].parent = i;
ht[rnode].parent = i;
}
}
}