5.5 Huffman编码
5.5.1 PFC编码及解码
0001 int main ( int argc, char* argv[] ) { //PFC编码、解码算法统一测试入口
0002 PFCForest* forest = initForest(); //初始化PFC森林
0003 PFCTree* tree = generateTree ( forest ); release ( forest ); //生成PFC编码树
0004 PFCTable* table = generateTable ( tree ); //将PFC编码树转换为编码表
0005 for ( int i = 1; i < argc; i++ ) { //对于命令行传入的每一明文串
0006 Bitmap codeString; //二进制编码串
0007 int n = encode ( table, codeString, argv[i] ); //将根据编码表生成(长度为n)
0008 decode ( tree, codeString, n ); //利用编码树,对长度为n的二进制编码串解码(直接输出)
0009 }
0010 release ( table ); release ( tree ); return 0; //释放编码表、编码树
0011 } //release()负责释放复杂结构,与算法无直接关系,具体实现详见代码包
5.5.2 最优编码树
同一字符集的所有编码方案中,平均编码长度最小者称作最优方案;对应编码树的ald()值也达到最小,故称之为最优二叉编码树,简称最优编码树(optimal encoding tree)。
-
双子性:首先,最优编码树必为真二叉树:内部节点左右孩子全双。
-
层次性:最优编码树中,叶节点位置的选取有严格限制——深度之差不得超过1。
上图中:交换y与子树p,平均编码长度更短。(交换:swap)
- 最优编码树的构造
由上可知,最优编码树中的叶节点只能出现于最低两层,故这类树的一种特例就是真完全树。由此,可以直接导出如下构造最优编码树的算法:创建一棵规模为2|Σ| - 1的完全二叉树T,再将Σ中的字符任意分配给T的|Σ|个叶节点。
5.5.3 Huffman编码树
-
字符出现概率
以上最优编码树算法的实际应用价值并不大,原因在于各字符在文本串中出现的频率不一致。
-
带权平均编码长度与叶节点带权平均深度
-
完全二叉树编码 != wald()最短
-
最优带权编码树
一方面,最优带权编码树必须满足双子性;
另一方面,尽管最优编码树不一定仍是完全的,却依然满足某种意义上的层次性。
-
层次性
字符出现概率越低,越处于底层。
5.5.4 Huffman编码算法
-
原理与构思
设字符x和y在Σ中出现的概率最低,考查另一字符集Σ' = (Σ \ {x, y}) ∪ {z}
其中\表示剔除的意思,而字符z出现的概率为 p(z) = p(x) + p(y)
操作方法:任取Σ'的一棵最优带权编码树T',于是根据层次性,只需将T'中与字符z对应的叶节点替换为内
部节点,并在其下引入分别对应于x和y的一对叶节点,即可得到Σ的一棵最优带权编码树。
-
策略与算法
已知:字符集Σ,其中所有字符概率已知
求:最优带权编码树
构造方法:
- 对Σ中每一字符分别建立一颗单个节点的树,其权重取作该字符的频率,这|Σ|棵树构成一个森林F
- 从F中选出权重最小的两棵树,创建一个新的节点,并分别以这两颗树作为其左、右子树,合并为一颗更高的树,其权重为二者之和。(合并的新树可以等效视作一个字符,称作超字符)
- 反复进行选取、合并的过程,每经过一轮迭代,F中的树就减少一颗。当最终F仅包含一棵树时,它就是一颗最优带权编码树
以上构造过程称作Huffman编码算法,由其生成的编码树称作Huffman编码树(Huffman encoding tree)。
需要强调的是,Huffman编码树只是最优带权编码树中的一棵。
-
总体框架
0001 /****************************************************************************************** 0002 * 无论编码森林由列表、完全堆还是左式堆实现,本测试过程都可适用 0003 * 编码森林的实现方式采用优先级队列时,编译前对应的工程只需设置相应标志: 0004 * DSA_PQ_List、DSA_PQ_ComplHeap或DSA_PQ_LeftHeap 0005 ******************************************************************************************/ 0006 int main ( int argc, char* argv[] ) { //Huffman编码算法统一测试 0007 int* freq = statistics ( argv[1] ); //根据样本文件,统计各字符的出现频率 0008 HuffForest* forest = initForest ( freq ); release ( freq ); //创建Huffman森林 0009 HuffTree* tree = generateTree ( forest ); release ( forest ); //生成Huffman编码树 0010 HuffTable* table = generateTable ( tree ); //将Huffman编码树转换为编码表 0011 for ( int i = 2; i < argc; i++ ) { //对于命令行传入的每一明文串 0012 Bitmap* codeString = new Bitmap; //二进制编码串 0013 int n = encode ( table, codeString, argv[i] ); //将根据编码表生成(长度为n) 0014 decode ( tree, codeString, n ); //利用Huffman编码树,对长度为n的二进制编码串解码 0015 release ( codeString ); 0016 } 0017 release ( table ); release ( tree ); return 0; //释放编码表、编码树 0018 } //release()负责释放复杂结构,与算法无直接关系,具体实现详见代码包 -
总体运行时间:每迭代一次,森林的规模减一,故共需迭代n - 1次,直到只剩最后一棵树。
“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天,点击查看活动详情”