算法基础(八):超详细最优二叉树构建(1)

时间:2021-11-15 03:58:13

赫夫曼(Huffman)树也称最有二叉树,是一类带全路径长度最短的树,有着广泛的应用。比如一棵判定树,根据学生的成绩划分及格还是不及格还是优、中等、良好。显然用if-else或者switch就可以简单实现,当然可以直接毫不考虑的直接这样写,但是如果我们再肯花点功夫,就可以得到更加高效的程序。我们可以以学生的总的学科分数的占的各个分数段的比率为权,构造一棵赫夫曼树,这样可以减少比较次数,提高程序运行效率。

构造赫夫曼树的方法步骤其实很简单--赫夫曼算法:

(1). 根据给定的n个权值{w1,w2,w3...,wn}构成n棵二叉树的集合F = {T1,T2,....Tn},其中每棵二叉树Ti中只有一个带权的wi的根节点,其左右子树为空。

(2). 在F中选取两棵根节点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根节点的权值为其左、右子树上根节点的权值之和。

(3). 在F中删除这两棵树,同时将新得到的二叉树加入到F中。

(4). 重复(2)、(3),知道F中含一棵树为止。

下面是构造HuffmanTree的代码实现:

#include"stdafx.h"
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<string.h>

typedef struct
{
unsigned int weight;
unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree; //动态分配数组,存储哈弗曼树
typedef char** HuffmanCode; //动态分配数组存储哈夫曼编码表

void MaoPao(HuffmanTree HT,int* arr,int number,int &s1,int &s2) //传入HT、数组首地址、数组中元素的个数:m
{
//只需进行两趟比较即可,得到两个最小数
int i,j,m;
printf("\n进入冒泡排序函数...");
for(i = 0;i<2;i++)
{
for(j = 0;j<number - i;j++) //number是数组中的实际元素个数..
{
// printf("\n进入循环");
//printf("\n arrj = %d,arrj+1 = %d",arr[j],arr[j+1]);
if(HT[ arr[j] ].weight < HT[ arr[j+1] ].weight)
{
m = arr[j];
arr[j] = arr[j+1];
arr[j+1] = m;
}
}
}
s1 = arr[number - 1];
s2 = arr[number];
printf("\n.........%d,%d",s1,s2);
}
void Select(HuffmanTree HT,int n,int &s1,int &s2) //n是总的节点数量
{
printf("\n进入Select函数");
int arr[20] = {0}; //存放parent为0的节点的编号
int m = 0;
for(int i = 1;i<=n;i++)
{
if(HT[i].parent == 0)
{
arr[m] = i; //从0开始存,那么数组中的元素个数就 = m
m++;
printf("\nparent = 0的节点号:%d",i);
if(m > 19)m = 19;
}
}
//比如n = 8,表示一共8个数,最后m++ = 8。实际数组是0~7的,那么m就是数组中的元素个数
//OK,找到parent = 0 的节点,下面选择weight最小的两个,该用什么算法呢?我用的是冒泡法,上浮两个最小数
MaoPao(HT,arr,m,s1,s2); //m是数组中的元素个数..数组从1开始,数组中的最后一个元素是m,倒数一个是m-1得到两个权值最小的节点


}


int HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int* w,int n) //n是叶子的数量,w存放n个字符(叶子)的权值
{
int m; //总共的节点数
int s1 = 0;
int s2 = 0; //存储选择到的节点编号
char* cd;
int c;
int f;
int start;
HuffmanTree p;
int i = 0;
if(n <= 1)
return 0;
m = 2 * n - 1;

HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode));

for(p = HT+1,i = 1;i <= n; ++i,++p,++w)//第一号单元空出来的,从1开始,这里是初始化操作,分两步,一步是有权值的,已不是没有权值的!用if也是可以的
{
p->weight = *w;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
for(;i <= m;++i,++p)
{
printf("a..aaaaaaa......i = %d",i);
p->weight = 0;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
printf("初始化完毕..");
//建立哈弗曼树
//8,这是后面测试的。
for(i = n+1;i<=m;++i) //这里要循环n-1次,也就是说,构造哈弗曼树需要同一个操作做n-1次,m = 2n-1,2n-1-(n+1)+1 = n-1
{
Select(HT,i-1,s1,s2);
printf("\n此时...i = %d,m = %d,,,被选中的编号..%d,%d",i,m,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;
}
return 1;

}

void ShowHT(HuffmanTree &HT,int n) //n是叶子的数量
{

int m = 2*n - 1; //总共的节点数量
for(int i = 1;i<=m;i++)
{
printf("\n编号:%d--Weight:%d Parent: %d LeftChild: %d RightChild;%d",i,HT[i].weight,HT[i].parent,HT[i].lchild,HT[i].rchild);
}
}
下面是main函数的测试:

#include"stdafx.h" 
//#include"BasicGraph2.h"
#include "HuffmanCode.h"
void main()
{
HuffmanTree HT;
HuffmanCode ch;
int n = 8;
int m = 15;
int arr[] = {5,29,7,8,14,23,3,11};
if(HuffmanCoding(HT,ch,arr,n))
ShowHT(HT,n);
}

运行结果:

算法基础(八):超详细最优二叉树构建(1)

图2:

算法基础(八):超详细最优二叉树构建(1)

说明:其实根据上面的权值,树有多种,但是WPL都是一样的。