一、一些基本概念
1、路径:从树中一个结点到另一个结点之间的分支构成这两个结点之间的路径。
2、路径长度:路径上的分支数目称作路径长度。
3、树的路径长度:从树根到每一结点的路径长度之和。
4、权:赋予某个实体的一个量,是对实体的某个或某些属性的数值化描述。
5、结点的带权路径长度:从该结点到树根之间的路径长度与结点上权的乘积。
6、树的带权路径长度:树中所有叶子结点的带权路径长度之和,通常记为:
7、哈夫曼树:假设有m个权值{w1,w2,…,wm},可以构造一棵含n个叶子结点的二叉树,每个叶子结点的权为wi,则其中带权路径长度WPL最小的二叉树称为最优二叉树或哈夫曼树。
注意:
(1)完全二叉树不一定是哈夫曼树;
(2)权值越大的结点越靠近根结点;
(3)哈夫曼树不唯一,但其树的带权路径长度一定相等;
二、构造哈夫曼树
哈夫曼树结点结构:
哈夫曼树的存储表示
typedef struct{
int weight; //结点的权值
int parent,lchild,rchild; //结点的双亲、左孩子、右孩子的下标
}HTNode,*HuffmanTree; //动态分配数组存储哈夫曼树
哈夫曼树结点个数:
例题:已知w=(5,29,7,8,14,23,3,11),生成一棵哈夫曼树,计算树的带权路径长度。并给出其构造过程中存储结构HT的初始状态和终结状态。
【算法描述】
void CreateHT(HuffmanTree &HT,int n)
{
if(n<=1)return ;
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;
}
for(i=1;i<=n;++i) //输入前n个结点的权值
{
cin>>HT[i].weight;
}
for(i=n+1;i<=m;++i)
{ //通过n-1次的选择、删除、合并来创建哈夫曼树
Select(HT,i-1,s1,s2);
//在HT[k](1<=k<=i-1)中选择两个其双亲域为0且权最小的结点,并返回他们在HT中的序号s1和s2
HT[s1}.parent=i;
HT[s2}.parent=i;
//得到新结点i,从森林中删除s1,s2,将s1和s2的双亲域由0改为1
HT[i].lchild=s1; //s1作为i的左结点
HT[i}.rchild=s2; //s2作为i的右结点
HT[i].weight=HT[s1].weight+HT[s2].weight; //i的权值为左右孩子权值之和
}
三、哈夫曼编码
将树的左分支标记为0,右分支标记为1;(左0右1)
权 | 哈夫曼编码 |
---|---|
5 | 0 0 0 0 |
3 | 0 0 0 1 |
11 | 0 0 1 |
23 | 0 1 |
29 | 1 0 |
14 | 1 1 1 |
7 | 1 1 0 0 |
8 | 1 1 0 1 |
四、哈夫曼树的创建
要求:
1、从键盘输入n, 以及n个字符的概率。
例如:已知某系统在通信联络中只可能出现n种字符,其概率分别为 0.05, 0.29, 0.07, 0.08, 0.14, 0.23, 0.03, 0.11,试设计哈夫曼编码创建哈夫曼树。
2、用顺序存储。
#include<iostream>
using namespace std;
//哈夫曼树的存储结构
typedef struct {
int weight; //结点的权重
int parent, lchild, rchild; //结点的双亲、左孩子、右孩子的下标
}HTNode,*HuffmanTree;
//封装两个最小结点
typedef struct {
int s1;
int s2;
}MIN;
//选择双亲为0且结点权值最小的两个结点
MIN Select(HuffmanTree HT, int n)
{
int min, secmin,s1,s2;
min = 10000;
secmin = 10000;
MIN code;
s1 = 1; s2 = 1;
for (int i = 1; i <= n; i++)
{
if (HT[i].parent == 0 && (HT[i].weight<min))
{
min = HT[i].weight;
s1 = i;
}
}
for (int i = 1; i <= n; i++)
{
if (HT[i].parent == 0 && (HT[i].weight<secmin) && (i != s1))
{
secmin = HT[i].weight;
s2 = i;
}
}
code.s1 = s1;
code.s2 = s2;
return code;
}
//创造哈夫曼树
void CreateHuffmanTree(HuffmanTree &HT, int num)
{
int m;
m = 2 * num - 1;
HT = new HTNode[m + 1];
for (int i = 1; i <= m; i++)
{
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
cout << "请输入每个叶子结点的权值:" << endl;
for (int i = 1; i <= num; i++)
{
cin >> HT[i].weight;
}
for (int i = num + 1; i <= m; i++)
{
MIN min;
min=Select(HT,i-1);
HT[min.s1].parent = i;
HT[min.s2].parent = i;
HT[i].lchild = min.s1;
HT[i].rchild = min.s2;
HT[i].weight = HT[min.s1].weight + HT[min.s2].weight;
}
//输出哈夫曼树存储结构的末态
for (int i = 1; i <= m; i++)
{
cout << "结点序号 " << i << " 权重 " << HT[i].weight << " parent " << HT[i].parent << " lchild " << HT[i].lchild << " rchild " << HT[i].rchild << endl;
}
}
int main()
{
cout << "开始创建哈夫曼树" << endl;
int num; //结点的个数
cout << "请输入哈夫曼树叶子结点的个数:";
cin >> num;
//创造哈夫曼树
HuffmanTree HT;
CreateHuffmanTree(HT, num);
return 0;
}