数据结构笔记之十赫夫曼树.doc
文本预览下载声明
36. 蛤蟆的数据结构笔记之三十六赫夫曼树
本篇名言:“假如生活欺骗了你,不要忧郁,也不要愤慨!不顺心的时候暂且容忍:相信吧,快乐的日子就会到来。 --普希金”
这篇我们来学习赫夫曼树,当然也是哈夫曼树,音译不同,大伙不用太较劲。
什么是赫夫曼呢?为什么叫赫夫曼树呢?
1. 简介
赫夫曼在上世纪五十年代初就提出这种编码时,根据字符出现的概率来构造平均长度最短的编码。它是一种变长的编码。在编码中,若各码字长度严格按照码字所对应符号出现概率的大小的逆序排列,则编码的平均长度是最小的。(注:码字即为符号经赫夫曼编码后得到的编码,其长度是因符号出现的概率而不同,所以说赫夫曼编码是变长的编码。) 而且赫夫曼编码是按照子树到父亲,而其读码则是完全相反的。
有点拗口,咱们就来看下基本概念。(代码均来自网络,由蛤蟆实测可用)
2. 基本概念
a、路径和路径长度
若在一棵树中存在着一个结点序列 k1,k2,……,kj, 使得?ki是ki+1?的双亲(1=ij),则称此结点序列是从 k1 到 kj 的路径。
从 k1 到 kj 所经过的分支数称为这两点之间的路径长度,它等于路径上的结点数减1.
b、结点的权和带权路径长度
在许多应用中,常常将树中的结点赋予一个有着某种意义的实数,我们称此实数为该结点的权,结点的带权路径长度规定为从树根结点到该结点之间的路径长度与该结点上权的乘积。
c、树的带权路径长度
树的带权路径长度定义为树中所有叶子结点的带权路径长度之和,公式如下图 1:
其中,n表示叶子结点的数目,wi 和 li 分别表示叶子结点 ki 的权值和树根结点到 ki 之间的路径长度。
如下图2
中树的带权路径长度 WPL = 9 x 2 + 12 x 2 + 15 x 2 + 6 x 3 + 3 x 4 + 5 x 4? =? 122
那什么是赫夫曼树呢?
3. 赫夫曼树
给定n个权值作为n的叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为赫夫曼树(Huffman tree)。赫夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
3.1 构造赫夫曼树
假设有n个权值,则构造出的赫夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则赫夫曼树的构造规则为:(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;(3)从森林中删除选取的两棵树,并将新树加入森林;(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的赫夫曼树。
?如:对 下图3中的六个带权叶子结点来构造一棵赫夫曼树,步骤如下:
注意:为了使得到的赫夫曼树的结构尽量唯一,通常规定生成的赫夫曼树中每个结点的左子树根结点的权小于等于右子树根结点的权。
3.2 赫夫曼编码
在电报通信中,电文是以二进制的0、1序列传送的,每个字符对应一个二进制编码,为了缩短电文的总长度,采用不等长编码方式,构造赫夫曼树,
将每个字符的出现频率作为字符结点的权值赋予叶子结点,每个分支结点的左右分支分别用0和1编码,从树根结点到每个叶子结点的路径上
所经分支的0、1编码序列等于该叶子结点的二进制编码。如上文所示的赫夫曼编码如下图4:
a 的编码为:00
b 的编码为:01
c 的编码为:100
d 的编码为:1010
e 的编码为:1011
f 的编码为:11
4. 赫夫曼树代码
4.1 定义结构体
节点结构体,包含一个整型和两个子叶的指针。
struct BTreeNode
{
ElemType data;
struct BTreeNode* left;
struct BTreeNode* right;
};
4.2 Main
输入叶子的节点数,如果n=1则重新输入,如果1则继续。
为n个节点分配存储空间,然后为每个子叶节点分配权值。
调用createHuffman函数创建赫夫曼树,然后输出赫夫曼树,输出带权路径长度,最后进行赫夫曼编码。
如下图5
4.3 CreateHuffman
输入参数为记录权值的数组和数组的个数。
根据数组个数创建n个存储空间。
然后数组b中的每个元素指向一个权值。
K1,k2表示最小和次小结点的下标。
为K1和K2的节点创建一个新的节点,其左节点为k1,右节点为k2.
通过n-1循环后,刚好剩下一个根节点。
4.4 PrintBTree_int
输入参数为根节点,然后输出根节点的值,如果左右节点都为NULL,则结束。
否则输出左子树(也是先输出左子树的根节点,在输入左子树的左子树),然后输出右子树。
4.5 Wei
显示全部