1.概念:
哈夫曼树(Huffman Tree)是一种常用的数据结构,用于实现数据压缩和编码。它是由美国计算机科学家David A. Huffman于1952年提出的,被广泛应用于通信、压缩算法和信息存储等领域。
2.性质:
1.哈夫曼树的结点的度数为0或2,没有度数为1的结点。
2.包含n个叶子结点的哈夫曼树中共有2n-1个结点。
3.其带权路径长度WPL最小的二叉树,且称为最优二叉树或哈夫曼树。
WPL = 权值 * 深度;
3.步骤
口诀:
①构造森林全是根
②选用两小造新树
③删除两小添新人
④重复②③操作,直到只剩一棵树
4.图例分析
以下图例能够简单且很直观得看出并推导出N个权值根的Huffmantree构造过程。
5.哈夫曼树算法分析
像我们已知哈夫曼树框架就是类似于连连看,先是一个个的独立的点(根),然后将其中点数最小的两个点,连接在一个新的点上,这个新的点的点数就是两个小点的点数之和,然后循环往复,获得最后一个点,此时这些点之间的关系就是哈夫曼树逻辑联系。
①构造森林全是根
第一步应该是先将一个个独立的点(根)创建出来,你得先知道这个点(根)它包含了哪些属性,作为一个程序开发者,你需要考虑的就是用什么变量来存放且描述这个点(根)
最简单的哈夫曼树他就是一颗最基本的二叉树,他拥有权值域,指针域---(父结点,左孩子结点,右孩子结点)
此时就可以考虑用结构体来存放这些点(根)
/*结构体*/
typedef struct {
int weight; //权值
int parent,lchild,rchild; //父节点和左右孩子节点
}HTNode, *HuffmanTree;
②选用两小造新树
如何选用两个权值最小的根去造新树,这是不用细说的吧,就是使用排序算法就可以了,找到最小的两个权值的下标,将其指针域中的父节点,指向新的结点,并使新的根左右孩子指向子节点!
③删除两小添新人
找到森林中最小的两个根作为子节点去构造一颗树,然后将之前的结点删除(删除肯定是不需要删除的,只是得有个标志位)
当其作为子节点去构造一颗树的时候,那它的指针域中的父结点,肯定不为空,这就是标志位!
②③步循环逻辑使用代码逻辑
循环使用for循环,使用的次数就是,要新建结点的个数
Select_min_tree(HT, i-1); //找到两个最小值的下标 s1,s2
HT[s1].parent = i;
HT[s2].parent = i;
HT[i].lchild = s1;
HT[i].rchild = s2;
HT[i].weight = HT[s1].weight+HT[s2].weight; //创造的森林根结点的权值为两个子节点的权值和
6.代码
代码以该图去编写代码,能理解该图基本就能懂上述讲解的逻辑思路
大家对于细节不懂的问题,第一可以试着去运行代码(调试运行)这样更能深刻,且贴切的理解代码的运行过程,以及地址空间的存取过程。
完整代码:
#include <stdio.h>
#include <stdlib.h>
/*口诀:
①构造森林全是根
②选用两小造新树
③删除两小添新人
④重复②③操作,直到只剩一棵树*/
//首先创建二叉树结构体
//创建函数---找出森林里最小的两颗树
//编写创建函数---两颗树变成一颗树,并删除两颗小树,添加合成之后的树到森林里面,直到只剩一棵树
/*结构体*/
typedef struct {
int weight; //权值
int parent,lchild,rchild; //父节点和左右孩子节点
}HTNode, *HuffmanTree;
//二维数组字符
int s1 ,s2 = 0; //创建两个全局变量存放下标值
/*init 初始化函数*/
HTNode *huffmantree_init(int n)
{
int i;
HTNode *HT = (HTNode*)malloc(sizeof(HTNode)); //申请2n-1个节点空间
//初始化哈夫曼树的各个节点,只需要2n-1个
for(i = 1; i<=(2*n-1); i++) //创造没有子森林的森林
{
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
if(i <= n)
{
printf("输入第%d颗树的权值:",i);
scanf("%d",&(HT[i].weight));
}
}
return HT;
}
/*select函数*/
int Select_min_tree(HTNode *T, int n) //找出森林里最小的两棵树
{
s1 = 0;
s2 = 0;
int min1,min2,i= 0;
for(i = 1;i <= n;i++) //初始化两个祖先变量
{
if(T[i].parent == 0 && s1 == 0) //前提是没有父节点,意思就是森林中的根结点
{
min1 = T[i].weight;
s1 = i;
}
else if(T[i].parent == 0 && s2 == 0) //前提是没有父节点,意思就是森林中的根结点
{
min2 = T[i].weight;
s2 = i;
if(min2 < min1)
{
min2 = min1 ;
min1 = T[i].weight;
s2 = s1;
s1 = i;
}
break;
}
}
for(i = 2;i < n;i++) //找出最小的两个值min1,min2
{
if((T[i].weight < min2) && T[i].parent == 0 && i != s1)
{
if(T[i].weight < min1)
{
min2 = min1;
s2 = s1;
min1 = T[i].weight;
s1 = i;
}
else
{
min2 = T[i].weight;
s2 = i;
}
}
}
}
//构建哈夫曼树
int CreateHuffmanTree(HTNode *HT, int n, int m)
{
//如果只有一棵树 那就直接跳出
if(n <= 1)
return 0;
int i;
for(i = n+1; i <= m; i++)
{
Select_min_tree(HT, i-1); //找到两个最小值的下标 s1,s2
HT[s1].parent = i;
HT[s2].parent = i;
HT[i].lchild = s1;
HT[i].rchild = s2;
HT[i].weight = HT[s1].weight+HT[s2].weight; //创造的森林根结点的权值为两个子节点的权值和
}
}
//打印函数Huffmantree();
void Print_huffmantree_demo(HTNode *HT,int n,int m)
{
int i ;
for(i = 0;i<=m; i++)
{
printf("权值:%d\n",HT[i].weight);
}
}
int main()
{
int n;
printf("请输入森林中子树的数量:");
scanf("%d",&n);
HTNode *HT;
int m = 2*n-1;
HT = huffmantree_init(n); //初始化huffmantree根结点
CreateHuffmanTree(HT,n,m); //创建huffmantree实现逻辑
Print_huffmantree_demo (HT,n,m); //打印huffmantree
return 0;
}