哈夫曼树相关概念
- 应用领域:压缩(提高了网络的传输效率)
- 编码依据:最优二叉树(哈夫曼树)
- 相关概念:
- 节点的权(w):赋予叶子结点有意义的值
- 节点的路径长度(L):从根节点到当前节点的个数
- 节点的带权路径长度:w*L
- 一棵二叉树的带权路径长度:二叉树的所有叶子节点的带权路径长度之和
- 最优二叉树:二叉树的带权路径长度最小
- 最优二叉树的特点:
- 最优二叉树没有单亲节点,只有双亲节点和叶子结点
- 若叶子节点的个数是n,那么双亲节点的个数是n-1
最优二叉树的构建总体思路
- 以每一个节点为根节点构建的树组成森林;
- 从森林中获取权值最小的和次小的二叉树,构成新的二叉树,再放回森林;
- 重复第二步,直至森林中只有一棵树。
哈夫曼树的实现
用链表存储哈夫曼树
存储形式:
节点:
森林:用数组存储
- 算法描述
1.定义节点类型
//节点的结构
typedef struct node{
struct node *left,*right;//储存指向左右节点的指针
char word;//存储节点的表示
int weight;//存储节点的权重
}HuffNode;
2.定义森林数组并初始化
HuffNode **F;//指向动态数组的指针
int n;//数组的长度
//从键盘输入数组长度
printf("请输入数组长度:");
scanf("%d",&n);
//定义储存森林的数组
F = (HuffNode **)malloc(n*sizeof(HuffNode*));
//初始化森林
for(int i = 0;i<n;i++){
int w;//表示权重
char ch;//表示节点
//新建二叉树节点
F[i] = (HuffNode*)malloc(sizeof(HuffNode));
printf("请输入节点的表示");
scanf("%c",ch);
printf("请输入节点的权重:");
scanf("%d",w);
F[i]->word = ch;
F[i]->weight = w;
F[i]->left = F[i]->right = NULL;
}
3.最优二叉树的构建
思路:输入的节点只能是叶子结点。所以构建二叉树的过程,就是创建双亲节点。根据最优二叉树的特点(若叶子节点的个数是n,那么双亲节点的个数是n-1),构建最优二叉树的循环次数为n-1次。而在每一循环中,都要从森林中获取最小以及次小的二叉树,作为新二叉树的左右节点,并返回森林,参与下一次循环
//构建最优二叉树
HuffNode *createHuffTree(HuffNode **F,int n){
int loop;//表示循环次数
HuffNode *newF; //表示新生成的节点
for(loop = 1;loop < n;loop++){
int k1,k2;//k1表示最小节点的位置,k2表示次小节点的位置
k1 = k2 = -1;
//获取最小以及次小节点位置的初值
for(int i = 0;i < n && k2 == -1;i++){
//F[i]=NULL时,表示该元素已经有双亲节点,不能参与最小以及次小的选取。
if(k1 == -1 && F[i]){
k1 = i;
}else if(F[i]){
k2 = i;
}
}
//获取最小以及次小节点的位置
for(int j = k2;j < n;j++){
if(F[j]){
if(F[j]->weight < F[k1]->weight){
k2 = k1;
k1 = j;
}else if(F[j]->weight < F[k2]->weight){
k2 = j;
}
}
}
//生成新的节点
newF = (HuffNode*)malloc(sizeof(HuffNode));
//将找到的最小以及次小节点挂到新的节点
newF->left = F[k1];
newF->right = F[k2];
newF->weight = F[k2]->weight + F[k1]->weight;
newF->word = ' ';
//将新生成的节点放回森林
F[k1] = newF;
F[k2] = NULL;
}
return newF;
}
- 用数组形式存储哈夫曼树
- 存储形式
- 节点
- 森林
- 节点
- 算法描述
1.定义节点的类型
- 存储形式
//定义节点的类型
typedef struct {
char word;//节点表示
int weight;//节点的权重
int parent,left,right;//用数组下标表示节点的双亲,左右子女节点
int *code;//用指针数组表示哈夫曼编码
}Huff;
2.定义森林数组并初始化
思路:在节点初始化过程中,左右子节点以及它的双亲节点设为-1
Huff *F;//表示节点
int n;//表示节点的个数
printf("请输入节点个数");
scanf("%d",&n);
F = (Huff *)malloc((2*n-1)* sizeof(Huff));//申明指针数组
//初始化节点并存入数组
for (int i = 0; i < n; ++i) {
int w;//表示权重
char ch;//表示节点
printf("请输入节点的表示");
scanf("%c",&ch);
printf("请输入节点的权重");
scanf("%d",&w);
F[i].weight = w;
F[i].word = ch;
F[i].left = F[i].parent = F[i].right = -1;
F[i].code = NULL;
}
3.构建最优二叉树
思路:
- 构造最优二叉树的过程,其实就是构造双亲节点的过程,根据最优二叉树的特点(若叶子节点的个数是n,那么双亲节点的个数是n-1)。故构造双亲节点的次数就是n-1。
- 节点的左右子节点以及双亲节点皆用数组的下标表示。
- 当节点的parent为-1时,表示还没有构造其双亲节点。若parent不为-1,则表示双亲节点在数组的位置,此节点不能参与构造双亲节点的过程中
//构造最优二叉树
void createHuffTree(Huff *F,int n){
//loop表示构造双亲节点的次数
//k1表示最小节点,k2表示次小节点
int loop,k1,k2,j;
k1 = k2 = -1;
for (loop = 0; loop < n-1; ++loop) {
//找到初始k1,k2的值
//注意创建的双亲节点,如果parent也为-1,同样也要参与最小值以及次小值的选取
for (int i = 0; i < loop+n && (k2 == -1); ++i) {
if(k1 == -1 && F[i].parent != -1){
k1 = i;
}else if (F[i].parent != -1) {
k2 = i;
}
}
//找出最小值以及次小值
for (j = 0; j < loop+n; ++j) {
if (F[j].parent !=-1){
if(F[j].weight < F[k1].weight){
k2 = k1;
k1 = j;
}else if(F[j].weight < F[k2].weight){
k2 = j;
}
}
}
//跳出循环后,j正好表示紧接着已分配的数组元素的尚未分配的数组元素
//双亲节点的设置
F[j].word = ' ';
F[j].left = k1;
F[j].right = k2;
F[j].weight = F[k1].weight + F[k2].weight;
F[j].parent = -1;
F[j].code = NULL;
//左右子节点双亲节点的设置
F[k1].parent = F[k2].parent = j;
}
}