一、哈夫曼树的构造
注意:哈夫曼树并不唯一,但带权路径长度一定是相同的。哈夫曼树构建转载该文
- (1)8个结点的权值大小如下:
- (2)从19,21,2,3,6,7,10,32中选择两个权小结点。选中2,3。同时算出这两个结点的和5。
- (3)从19,21,6,7,10,32,5中选出两个权小结点。选中5,6。同时计算出它们的和11。
- (4)从19,21,7,10,32,11中选出两个权小结点。选中7,10。同时计算出它们的和17。
(BTW:这时选出的两个数字都不是已经构造好的二叉树里面的结点,所以要另外开一棵二叉树;或者说,如果两个数的和正好是下一步的两个最小数的其中的一个,那么这个树直接往上生长就可以了,如果这两个数的和比较大,不是下一步的两个最小数的其中一个,那么就并列生长。
- (5)从19,21,32,11,17中选出两个权小结点。选中11,17。同时计算出它们的和28。
- (6)从19,21,32,28中选出两个权小结点。选中19,21。同时计算出它们的和40。另起一颗二叉树。
- (7)从32,28, 40中选出两个权小结点。选中28,32。同时计算出它们的和60。
- (8)从 40, 60中选出两个权小结点。选中40,60。同时计算出它们的和100。 好了,此时哈夫曼树已经构建好了。
二、代码实现
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cstdio>
using namespace std;
struct BTreeNode
{
int data;
struct BTreeNode* left;
struct BTreeNode* right;
};
//一般打印输的结点
void Print_BTree(struct BTreeNode* BT)
{
if (BT != NULL)
{
printf("%d", BT->data); //输出根结点的值
if (BT->left != NULL || BT->right != NULL)
{
printf("(");
Print_BTree(BT->left); //输出左子树
if (BT->right != NULL)
printf(",");
Print_BTree(BT->right); //输出右子树
printf(")");
}
}
}
//创建一颗哈夫曼树
struct BTreeNode* CreateHuffman(int a[],int n)
{
struct BTreeNode **b;//结构体指针的指针b作为指针数组
struct BTreeNode *q;//定义一个结构体P
b=(BTreeNode**)malloc(n*sizeof(struct BTreeNode*));
//对指针数组b进行初始化
for(int i=0;i<n;i++)//初始化b指针数组,使每个指针元素指向a数组中对应的元素结点
{
b[i]=(BTreeNode*)malloc(n*sizeof(struct BTreeNode*));
b[i]->data=a[i];
b[i]->left=NULL;
b[i]->right=NULL;
}
//利用循环创建一颗哈夫曼树
for(int i=1;i<n;i++)//一共进行n-1次循环
{
int k1=-1,k2;
for(int j=0;j<n;j++)//让k1初始指向森林中第一棵树,k2指向第二棵
{
if(b[j]!=NULL&&k1==-1)
{
k1=j;
continue;
}
if(b[j]!=NULL)
{
k2=j;
break;
}
}
//从当前除过第一个结点之外的最小的那个结点
for(int j=k2;j<n;j++)
{
if(b[j]!=NULL)
{
if(b[j]->data<b[k1]->data)
{
k2=k1;
k1=j;
}
if(b[j]->data>b[k2]->data)
{
k2=j;
}
}
}
//利用最小值数和次小值树,建立哈夫曼树,将每次返回树根指针q;
q=(BTreeNode*)malloc(sizeof(struct BTreeNode));
q->data=b[k1]->data+b[k2]->data;//树根结点存放的是总路径
q->left=b[k1];
q->right=b[k2];
b[k1]=q;//更新结点(将最新的根节点付给b[k1]当作是新的根节点)
b[k2]=NULL;//将b[k2]指针置空
}
free(b);//将建立的动态数组释放
return q;//放回根节点的指针
}
//求哈夫曼树的路径长度
int WeightPathLength(struct BTreeNode* FBT, int len)//len初始为0
{
if (FBT == NULL) //空树返回0
return 0;
else
{
if (FBT->left == NULL && FBT->right == NULL)//访问到叶子结点
return FBT->data * len;
else //访问到非叶子结点,进行递归调用,返回左右子树的带权路径长度之和,len递增
return WeightPathLength(FBT->left,len+1)+WeightPathLength(FBT->right,len+1);
}
}
//哈夫曼树解码
void Coding(struct BTreeNode *FBT,int len)
{
static int a[10];//用来存储密码0 1
if(FBT!=NULL)
{
if(FBT->left==NULL&&FBT->right==NULL)
{
printf("j结点权值为 %d 的编码:",FBT->data);
for(int i=0;i<len;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
else
{//遍历的层数加1
a[len]=0;//左节点为0
Coding(FBT->left,len+1);//采用递归实现
a[len]=1;//右结点为1
Coding(FBT->right,len+1);
}
}
}
//主函数
int main()
{
int n;
static int a[10];
struct BTreeNode *fbt;
cout<<"请输入节点数n :";
cin>>n;//输入一共有多少个结点
cout<<"请输入"<<n<<"个数作为权值:";
for(int i=0;i<n;i++)//输入节点数
{
cin>>a[i];//作为权值
}
fbt=CreateHuffman(a,n);
fbt = CreateHuffman(a, n);
cout<<"打印一般的树的:";
Print_BTree(fbt);
cout<<endl;
int t=WeightPathLength(fbt,0);
cout<<"哈夫曼树的路径长度:"<<t<<endl;
cout<<"哈夫曼树编码:"<<endl;
Coding(fbt,0);
return 0;
}