【哈夫曼树】
当用 n 个结点(都做叶子结点且都有各自的权值)试图构建一棵树时,如果构建的这棵树的带权路径长度最小,称这棵树为“最优二叉树”,有时也叫“赫夫曼树”或者“哈夫曼树”
构建哈夫曼树:
1.在 n 个权值中选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新二叉树的根结点的权值为左右孩子权值的和;
2.在原有的 n 个权值中删除那两个最小的权值,同时将新的权值加入到 n–2 个权值的行列中,以此类推;
3.重复 1 和 2 ,直到所以的结点构建成了一棵二叉树为止,这棵树就是哈夫曼树。
哈夫曼树结点结构:
typedef struct Huffman{
int weight;//权重
int parent,lch,rch;//父结点、左孩子、右孩子在数组中的位置下标
}HTNode,*HuffmanTree;
找到两个最小值:
//HT数组中存放的哈夫曼树,s1和s2传递的是HT数组中权重值最小的两个结点在数组中的位置
void select_Tree(HuffmanTree HT,int n,int &s1,int &s2)
{
int min;
int i;
//对找到的两个结点比较大小,min2为大的,min1为小的
printf("select\r\n");
//找第一个最小值
for(i = 1;i <= n;i++)
{
if(HT[i].parent == 0)
{
min = i;
break;
}
}
for(i = min+1;i <= n;i++)
{
if(HT[i].parent == 0&&HT[i].weight < HT[min].weight )
{
min = i;
}
}
s1 = min;
printf("s1 = %d\n",s1);
//找第二个最小值
for(i = 1;i <= n;i++)
{
if(HT[i].parent == 0&&i != s1)
{
min = i;
break;
}
}
for(i = min+1;i <= n;i++)
{
if(HT[i].parent == 0 && HT[i].weight <= HT[min].weight && min != s1)
{
min = i;
}
}
s2 = min;
printf("s2 = %d\n",s2);
}
创建哈夫曼树:
//HT为地址传递的存储哈夫曼树的数组,n为结点个数
void creat_Tree(HuffmanTree HT,int n)
{
int m,i;
int s1,s2;
printf("creat\r\n");
if(n <= 1)
{
return;// 如果只有一个编码就相当于0
}
m = 2*n - 1;//数组一共有2n-1个元素;
HT = (HuffmanTree)calloc(m+1,sizeof(Huffman));// 0号位置不用
for(i = 1;i <= m ;i++)
{
HT[i].lch = 0;
HT[i].rch = 0;
HT[i].parent = 0;
}
printf("in weight\n");
for(i = 1;i<=n;i++)
{
std::cin>>HT[i].weight ;
}//输入权重
printf("111\r\n");
for(i = n+1;i <= m;i++)//合并产生n-1个节点-构造哈夫曼树
{
select_Tree(HT,i-1,s1,s2);//在ht[k](1<=k<=i-1),找最小且双亲域为0的两个节点,返回它们在ht中的序号
HT[i].weight = HT[s1].weight + HT[s2].weight ;//i的权值为s1和s2之和
HT[s1].parent = i;
HT[s2].parent = i;//从ht中删除s1和s2
HT[i].lch = s1;
HT[i].rch = s2;
printf("HT[%d].weight = %d,HT[%d].weight = %d\r\n",s1,HT[s1].weight,s2,HT[s2].weight );
printf("end of %d\r\n HT[%d].weight = %d\r\n",i,i,HT[i].weight );
}
printf("哈夫曼树为:>\n");
printf("下标 权值 父结点 左孩子 右孩子\n");
printf("0 \n");
for (int i = 1; i <= m; i++)
{
printf("%-4d %-6d %-6d %-6d %-6d\n", i, HT[i].weight, HT[i].parent, HT[i].lch, HT[i].rch);
}
printf("\n");
}
总代码:
#include <stdio.h>
#include <malloc.h>
#include <iostream>
//哈夫曼树结点结构
typedef struct Huffman{
int weight;//权重
int parent,lch,rch;//父结点、左孩子、右孩子在数组中的位置下标
}HTNode,*HuffmanTree;
//HT数组中存放的哈夫曼树,s1和s2传递的是HT数组中权重值最小的两个结点在数组中的位置
void select_Tree(HuffmanTree HT,int n,int &s1,int &s2)
{
int min;
int i;
//对找到的两个结点比较大小,min2为大的,min1为小的
printf("select\r\n");
//找第一个最小值
for(i = 1;i <= n;i++)
{
if(HT[i].parent == 0)
{
min = i;
break;
}
}
for(i = min+1;i <= n;i++)
{
if(HT[i].parent == 0&&HT[i].weight < HT[min].weight )
{
min = i;
}
}
s1 = min;
printf("s1 = %d\n",s1);
//找第二个最小值
for(i = 1;i <= n;i++)
{
if(HT[i].parent == 0&&i != s1)
{
min = i;
break;
}
}
for(i = min+1;i <= n;i++)
{
if(HT[i].parent == 0 && HT[i].weight <= HT[min].weight && min != s1)
{
min = i;
}
}
s2 = min;
printf("s2 = %d\n",s2);
}
//HT为地址传递的存储哈夫曼树的数组,n为结点个数
void creat_Tree(HuffmanTree HT,int n)
{
int m,i;
int s1,s2;
printf("creat\r\n");
if(n <= 1)
{
return;// 如果只有一个编码就相当于0
}
m = 2*n - 1;//数组一共有2n-1个元素;
HT = (HuffmanTree)calloc(m+1,sizeof(Huffman));// 0号位置不用
for(i = 1;i <= m ;i++)
{
HT[i].lch = 0;
HT[i].rch = 0;
HT[i].parent = 0;
}
printf("in weight\n");
for(i = 1;i<=n;i++)
{
std::cin>>HT[i].weight ;
}//输入权重
printf("111\r\n");
for(i = n+1;i <= m;i++)//合并产生n-1个节点-构造哈夫曼树
{
select_Tree(HT,i-1,s1,s2);//在ht[k](1<=k<=i-1),找最小且双亲域为0的两个节点,返回它们在ht中的序号
HT[i].weight = HT[s1].weight + HT[s2].weight ;//i的权值为s1和s2之和
HT[s1].parent = i;
HT[s2].parent = i;//从ht中删除s1和s2
HT[i].lch = s1;
HT[i].rch = s2;
printf("HT[%d].weight = %d,HT[%d].weight = %d\r\n",s1,HT[s1].weight,s2,HT[s2].weight );
printf("end of %d\r\n HT[%d].weight = %d\r\n",i,i,HT[i].weight );
}
printf("哈夫曼树为:>\n");
printf("下标 权值 父结点 左孩子 右孩子\n");
printf("0 \n");
for (int i = 1; i <= m; i++)
{
printf("%-4d %-6d %-6d %-6d %-6d\n", i, HT[i].weight, HT[i].parent, HT[i].lch, HT[i].rch);
}
printf("\n");
}
int main()
{
int n;
HuffmanTree HT;
printf("请输入数据个数:\r\n");
scanf("%d",&n);
creat_Tree(HT,n);
}
运行结果:
请输入数据个数:
5
creat
in weight
1 5 7 2 10
111
select
s1 = 1
s2 = 4
HT[1].weight = 1,HT[4].weight = 2
end of 6
HT[6].weight = 3
select
s1 = 6
s2 = 6
HT[6].weight = 3,HT[6].weight = 3
end of 7
HT[7].weight = 6
select
s1 = 2
s2 = 7
HT[2].weight = 5,HT[7].weight = 6
end of 8
HT[8].weight = 11
select
s1 = 3
s2 = 5
HT[3].weight = 7,HT[5].weight = 10
end of 9
HT[9].weight = 17
哈夫曼树为:>
下标 权值 父结点 左孩子 右孩子
0
1 1 6 0 0
2 5 8 0 0
3 7 9 0 0
4 2 6 0 0
5 10 9 0 0
6 3 7 1 4
7 6 8 6 6
8 11 0 2 7
9 17 0 3 5
在构建哈弗曼树时,要使树的带权路径长度最小,只需要遵循一个原则,那就是:权重越大的结点离树根越近。