树和二叉树
Huffman 树
赫夫曼(Huffman)树,又称最优树,是一类带权路径长度最短的树,有着广泛的应用。
路径:从树中一个结点到另一个结点之间的分支构成这两个结点之间的路径。
路径长度:路径上分支的数目构成路径长度。
树的路径长度:树根到每一个节点的路径长度之和。
树的带权路径长度:树中所有叶子节点的带权路径长度之和,记作 W P L = ∑ k = 1 n w k l k WPL = \sum_{k=1}^{n}w_kl_k WPL=∑k=1nwklk
最优二叉树或赫夫曼树:假设有n个权值
{
w
,
w
,
.
.
.
,
w
.
}
\left \{w,w,...,w.\right \}
{w,w,...,w.},试构造一棵有n个叶子结点的二叉树,每个叶子结
点带权为
w
i
w_i
wi,则其中带权路径长度WPL最小的二叉树称做最优二叉树或赫夫曼树。
构造霍夫曼树
霍夫曼最早给出了一个带有一般规律的算法,俗称霍夫曼算法。
现叙述如下:
(1)根据给定的n个权值
(
w
1
,
w
2
,
.
.
.
,
w
n
)
(w_1,w_2,...,w_n)
(w1,w2,...,wn)构成n棵二叉树的集合
F
=
{
T
1
,
T
2
,
…
T
n
}
F=\left \{T_1,T_2,…T_n\right \}
F={T1,T2,…Tn},其中每棵二叉树
T
i
T_i
Ti中只有一个带权为
w
i
w_i
wi的根结点,其左右子树均空。
(2)在F中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左、右子树上根结点的权值之和。
(3)在F中删除这两棵树,同时将新得到的二叉树加入F中。
(4)重复(2)和(3),直到F只含一棵树为止。这棵树便是赫夫曼树。
Huffman编码
前缀编码:要设计长短不等的编码,则必须是任一个字符的编码都不是另一个字符的编码的前缀,这种编码称做前缀编码。
//-----赫夫曼树和赫夫曼编码的存储表示-----
typedef struct {
unsigned int weight;
unsigned int parent, lchild, rchild;
}HTNode, *HuffmanTree; //动态分配数组存储霍夫曼树
typedef char * * HuffmanCode; // 动态分配数组存储霍夫曼编码表
//求huffman编码的算法如下所示
void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC,int *w, int n){
// w 存放 n个字符的权重(均>0),构造huffman树HT,并求出 n个字符的huffman编码HC
if (n<=1) return ; // 节点小于等于 1的返回None
m = 2 * n -1;// 树节点的个数
HT = (HuffmanTree)malloc((m+1) * sizeof(HTNode)); //0号单元未用 ,申请(m+1)*htnode尺寸的内存
for (p = HT, i = 1; i<=n; ++i,++p,++w) *p = {*w, 0, 0 , 0};// 构造n个二叉树,值为w;parent,lchild,rchild都为 0
for (;i<=m; ++i ,++p) *p = {0,0,0,0};// 从n到m之间的二叉树权重,parent,lchild,rchild 都置为0
for (i = n+1; i<=m;++i){//建huffman树
//在HT[1..i-1]选择parent 为0 且weight 最小的两个节点,其序号分别为s1和s2.
Select(HT,i-1, s1,s2);
HT[s1].parent=i;HT[s2].parent = i; // 最小的两个节点其父节点置为i,
HT[i].lchild = s1; HT[i].rchild = s2; // 节点i的左节点置位s1,右节点置为s2
HT[i].weight = HT[s1].weight+ HT[s2].weight; //节点i的权重为 左右子节点权值相加
}
//---从叶子到根逆向求每个字符的赫夫曼编码---
HC=(HuffmanCode)malloc((n+1)*sizeof(char*));//分配n个字符编码的头指针向量
cd=(char*)malloc(n*sizeof(char));
//分配求编码的工作空间
cd[n-1]="\0";
//编码结束符。
for(i=1;i<=n;++i){
//逐个字符求赫夫曼编码
start=n-1;
//编码结束符位置
for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)//从叶子到根逆向求编码
if(HT[f].lchild==c) cd[--start]="0";
else cd[--start]="1";
HC[i]=(char*)malloc((n-start)*sizeof(char));//为第i个字符编码分配空间
strcpy(HC[i],&cd[start]); //从cd复制编码(串)到HC
}
free(cd);//释放工作空间
}//HuffmanCoding
例题
例 已知某系统在通信联络中只可能出现8种字符,其概率分别为0.05,0.29,0.07,0.08,0.14,0.23,0.03,0.11,试设计赫夫曼编码。
设权w=(5,29,7,8,14,23,3,11),n=8,则m=15,按上述算法可构造一棵赫夫曼树如图6.26所示。其存储结构HT的初始状态如图6.27(a)所示,其终结状态如图6.27(b)所示,所得赫夫曼编码如图6.27 (c)所示。