一、哈夫曼(Huffman)树的基本概念
1、树的带权路径长度:树中所有叶子结点的带权路径长度之和;
2、哈夫曼树:带权路径长度WPL最小的二叉树称作最优二叉树或哈夫曼树。
例如:
二、哈夫曼树的构造算法实现
(1)Huffman树中结点的结构可设计成4分量形式:
typedef struct{
int weight;//权值分量(可放大取整)
int parent,lchild,rchild; //双亲和孩子分量
}HTNode,*HuffmanTree;//用动态数组存储Huffman树
哈夫曼树的各个节点就存储在HuffmanTree定义的动态数组中,数组0号单元不用,从1号开始使用。将叶子节点集中存储在数组前面的n个位置。
(2)算法思想
1.初始化:动态申请2n个单元,将HT[1…2n-1]中的lchild=rchild=parent=0; 输入n个叶子结点的权值,即设置HT[1…n]的weight值。
void CreatHuffmanTree(HuffmanTree &HT, int n){//算法5.10 创建哈夫曼树
if (n<=1) return ;
int m = 2*n-1;
HT = new HTNode[m+1];
for(i=1;i<=m;++i)//将1~m号单元中的双亲、左孩子,右孩子的下标都初始化为0
{
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
cout<<"请输入叶子结点的权值:"<<endl;
for(i = 1; i<=n;++i )
{
cin>>HT[i].weight;
}//算法未完待续。。。
//未完待续......
初始化结果如图所示:
2.创建树:进行以下n-1次合并,依次产生HT[i],i=n+1…2n-1:
①在HT[1…i-1]中选两个未被选过的weight最小的两个结点HT[s1]和HT[s2] (从parent = =0 的结点中选)
②修改HT[s1]和HT[s2]的parent值: parent=i
③置HT[i].weight=HT[s1].weight + HT[s2].weight ,
HT[i].lchild=s1, HT[i].rchild=s2。
for(i=n+1; i<=m; ++i)
{
Select(HT,i-1,s1,s2) ;//在HT[k](1≤k≤i-1)中选择两个其双亲域为0, 且权值最小的结点, 并返回它们在HT中的序号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;
}
}
创建结果如图所示:
补充:创建树算法中的Select(HT,i-1,s1,s2)函数:
在HT[k](1≤k≤i-1)中选择两个其双亲域为0, 且权值最小的结点, 并返回它们在HT中的序号s1和s2。设置两个最小变量min1和min2(先对min1和min2赋最大值,其int型中最大值)。
void Select(HuffmanTree &HT,int n,int &s1,int &s2){
//选择权值最小的两个结点
int i;
int min1 = 2147483647, min2 = 2147483647;//先赋予最大值
for(i=1;i<=n;i++)
{
if(HT[i].weight<min1&&HT[i].parent==0)
{
min1=HT[i].weight;
s1=i;
}
}
for(i=1;i<=n;i++)
{
if(HT[i].weight<min2&&HT[i].parent==0&&i!=s1)
{
min2=HT[i].weight;
s2=i;
}
}
}
三、构造Huffman树的完整代码
C++代码实现,可经过简单修改,同样可以C语言实现
#include<iostream>
#include<string.h>
using namespace std;
typedef struct{
int weight;
int parent, lchild, rchild;
}HTNode,*HuffmanTree;
void Select(HuffmanTree &HT,int n,int &s1,int &s2){
//选择权值最小的两个结点
int i;
int min1 = 2147483647, min2 = 2147483647;//先赋予最大值
for(i=1;i<=n;i++)
{
if(HT[i].weight<min1&&HT[i].parent==0)
{
min1=HT[i].weight;
s1=i;
}
}
for(i=1;i<=n;i++)
{
if(HT[i].weight<min2&&HT[i].parent==0&&i!=s1)
{
min2=HT[i].weight;
s2=i;
}
}
}
//构造哈夫曼树
void CreatHuffmanTree(HuffmanTree &HT, int n){//算法5.10 创建哈夫曼树
if (n<=1) return ;
int m = 2*n-1;
int i,s1,s2;
HT = new HTNode[m+1];
for(i=1;i<=m;++i)//将1~m号单元中的双亲、左孩子,右孩子的下标都初始化为0
{
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
cout<<"请输入叶子结点的权值:"<<endl;
for(i = 1; i<=n;++i )
{
cin>>HT[i].weight;
}
for(i=n+1; i<=m; ++i)
{
Select(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;
}
}
void outputHuffman(HuffmanTree HT, int m){//输出哈夫曼树
cout<<"结点\t"<<"权\t"<<"双亲\t"<<"左孩子\t"<<"右孩子\n";
for(int i =1;i<=m;i++){
cout<<i<<"\t";
cout<<HT[i].weight<<"\t";
cout<<HT[i].parent<<"\t";
cout<<HT[i].lchild<<"\t";
cout<<HT[i].rchild<<"\n";
}
}
int main()
{ HuffmanTree HT;
int n,m;
cout<<"请输入叶子结点的个数:\n";
cin>>n;
CreatHuffmanTree(HT,n);
m=2*n-1;
outputHuffman(HT,m);
delete[] HT; // 释放内存
return 0;
}
四、实验结果
从实验结果可以看出,成功实现了HuffmanTree的构建。哈夫曼编码的实现:哈夫曼编码
嘿 兄弟,很高兴在这里遇见你,美好的事情即将发生~