【哈夫曼树】

当用 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

在构建哈弗曼树时,要使树的带权路径长度最小,只需要遵循一个原则,那就是:权重越大的结点离树根越近。