2018.5.16
最近一段时间忙于实验室各种项目和辅导员的各种杂活,间隔了半周没有耐下心学习。导师最近接了一个要PK京东方的项目让我来做总负责,确实是很惊喜了。责任心告诉我不能把工作做水了,但是还是尝试把实权移交给师兄们比较好。
这道题可以说是树这块的压轴题了,无论是代码量还是思维难度都和其他题目不在一个档次。题目意思是给定一个带权的输入序列,和N个与带权输入序列元素相同的编码测试序列,若编码符合最优编码,则输出Yes,若不符合,则输出No。
这道题考察最优编码长度,实际上是在考察Huffman树与Huffman编码,出题人担心你想不到,还特地在题干开头专门介绍了David A. Huffman和他提出的"A Method for the Construction of Minimum-Redundancy Codes"(一种实现最小冗余编码结构的方法),也就是Huffman Codes(哈夫曼编码)。
对于每个给定的带权元素序列来说,其必定可以建成一棵Huffman树,尽管根据建树方法的不同,树的结构不同,但是对于这个给定的带权元素序列,其最优编码长度是固定的,即某一形式的Huffman树下的Huffman编码长度。(建立Huffman树的过程为,每次将权重最小的两个结点合成一棵二叉树,其树根结点权值为两子树权值之合。再将该二叉树当作结点进行重新合并。重复上述过程直到使用完所有结点,建立成一棵树(理论上N个结点要进行N-1次合并))但需要注意的是,Huffman编码的编码长度是最优编码长度,但是最优编码长度可以不是其Huffman编码(题目最后一句也有特地提醒,看来出题人还是比较好心的)。因此,验证输入测试序列需要靠两点:1.符合最优编码长度,2.能够无歧义解码。
那么思路就出来了。先根据输入序列建立Huffman树,并获得最优编码长度。再对提交数据进行检查:1.是否符合最优编码长度,2.是否符合无歧义解码规则(前缀码编码,数据仅存在于二叉树叶节点)。
获得最优编码长度的过程,需要先创建一棵Huffman树,又需要先将带权序列建立成最小堆,再每轮弹出2次最小堆的顶点,作为二叉树的左右子树进行合并,合并完后的二叉树进行权值更新,再继续放入最小堆进行合并……直到最小堆元素全部弹出,最后弹出一整棵Huffman树。(最小堆的建立可以参见:#数据结构与算法学习笔记#PTA14:最小堆与最大堆(C/C++))。计算每一个测试序列的编码长度,与标准Huffman编码长度比较即可。最小堆每次插入和弹出都需要对全堆某个路径(根节点到叶子结点的一条路径)进行一次调整,具体情况分析详见代码注释。
检查前缀码编码的过程,需要根据输入序列的每个元素编码,模拟其在树中的路径(相当于每次创建一个元素编码所代表一条二叉树的路径,0代表左子树,1代表右子树)。模拟过程中的两种情况可以验证不满足前缀码要求(如下图):1.后创建的分支经过或超过已经被定义的叶子结点,2.后创建分支创建结束时未达到叶子结点。具体情况分析详见代码注释。
题目要求:
In 1953, David A. Huffman published his paper "A Method for the Construction of Minimum-Redundancy Codes", and hence printed his name in the history of computer science. As a professor who gives the final exam problem on Huffman codes, I am encountering a big problem: the Huffman codes are NOT unique. For example, given a string &