算法导论15.3 Exercises 答案
15.3-1
由于$x.freq\le y.freq$,因此$x.freq$是$C$中频率的最小值,此时$y.freq$是$C$中频率的次小值。由于$a.freq\le b.freq$,并且有$x.freq=b.freq$,那么有$x.freq=a.freq$。由于$y.freq$是$C$中的次小值,并且$C$中已经有$3$个频率值是最小且相等的,因此$y.freq=b.freq$。最终有$x.freq=y.freq=a.freq=b.freq$。
15.3-2
令$T$为这一棵非满二叉树,并且$u$是某一个只包含一个子节点的节点,那么考虑删除节点$u$,并将$u$的这个儿子拼接到其父节点中,得到一棵新树$T’$。这棵树$T’$将会比$T$更加优秀,证明过程是考虑比较$B(T)$和$B(T’)$的大小。
注意到,由于$u$节点被删除,此时以$u$为子树的所有节点的深度都减少了$1$。那么有
$\begin{aligned}
B(T’)&=\sum{c\in C} c.freq\cdot d{T’}(x)\
&=\sum{u\text{ is an ancestor of }x} x.freq\cdot d{T’}(x)+\sum{u\text{ isn’t an ancestor of }x} x.freq\cdot d{T’}(x)\
&=\sum{u\text{ is an ancestor of }x} x.freq\cdot d{T}(x)+\sum{u\text{ isn’t an ancestor of }x} x.freq\cdot (d{T}(x)-1)\
&<\sum{u\text{ is an ancestor of }x} x.freq\cdot d{T}(x)+\sum{u\text{ isn’t an ancestor of }x} x.freq\cdot d{T}(x)\
&=B(T)
\end{aligned}$
由此可知,我们构造出了一棵更优秀的树$T’$。因此一个非满二叉树必定不可能对应一个最优无前缀编码。
15.3-3
由此可以产生出如下的哈夫曼树。
graph TD A[a:1] B[b:1] C[c:2] D[d:3] E[e:5] F[f:8] G[g:13] H[h:21] ab((2)) ac((4)) ad((7)) ae((12)) af((20)) ag((33)) ah((54)) ah--0---H ah--1---ag ab--0---B ab--1---A ac--0---C ac--1---ab ad--0---D ad--1---ac ae--0---E ae--1---ad af--0---F af--1---ae ag--0---G ag--1---af
由此可以构造出哈夫曼编码:
$\begin{array}{|l|l|}
\hline
a & 1111111\
\hline
b & 1111110\
\hline
c & 111110\
\hline
d & 11110\
\hline
e & 1110\
\hline
f & 110\
\hline
g & 10\
\hline
h & 0\
\hline
\end{array}$
对于一般情况而言,如果现在有$n$个字符,第$n$个字符的频率为$fn(f_1=f_2=1,f_n=f{n-1}+f_{n-2})$,那么可以按照如下构造哈夫曼编码:第$1$个字符的哈夫曼编码为$1^{n-1}$,对于第$m(m>1)$个字符,其哈夫曼编码为$1^{n-m} \mid\mid 0$。
接下来通过说明算法HUFFMAN的运行结果说明这个结论是正确的。
首先证明:$\displaystyle{Fn=\sum{i=1}^n fi = f{i+2}-1}$。考虑使用数学归纳法进行证明。
当$n=1$时,$F_1=f_3-1=2-1=1$,成立。
当$n>1$时,假设对于$m=1,2,\dots,m-1$,$Fm=f{m+2}-1$都成立。那么有$Fn=F{n-1}+fn=f{n+1}-1+fn=f{n+2}-1$。结论仍然成立。
因此,$Fn=f{n+2}-1$成立。
接下来证明循环不变量:最小优先队列中的关键字按顺序总是${fi,F{i-1},f{i+2},f{i+3},\dots,fn}$,并且用$F$表示的关键字$F{i-1}$要么是最小值,要么是次小值。这个序列已经有序的原因是,当$i\ge 2$时,$F{i-1}=f{i+1}-1=fi+(f{i-1}-1)\ge fi,F{i-1}=f{i+1}-1<f{i+2}$。
因此,算法HUFFMAN总是弹出$Q$中用$F$表示的那个关键字,最终变成了一个新关键字$Fi=F{i-1}+f_i$。最终产生的编码如上述结论。
初始化:$Q$中的元素一开始为${f_2,f_1,f_3,\dots,f_n}$,将$f_1$用$F_1$代替后,循环不变量成立。
保持:若$Q$中的元素当前为${fi,F{i-1},f{i+1},f{i+2},\dots,fn}$,那么弹出最小的两个元素$f_i,F{i-1}$,构造出新元素$Fi=F{i-1}+fi$后,再添加进$Q$中,得到${f{i+1},F{i},f{i+2},f_{i+3},\dots,f_n}$,根据上面的结论可知,这个序列仍然是有序的。在这个保持过程中,用$F$表示的那个关键字被弹出来,这个用$F$表示的关键字添加进$Q$后,仍然是$Q$中最小或者是次小的数,在下一次仍然会被弹出。
终止:$Q$中只剩下一个元素$F_n$,整个序列仍然是有序的。
因此,用$F$表示的关键字不断被弹出、加入,最终构造出的哈夫曼树的所有内部节点至少都有一个叶子节点,因此构造出来的哈夫曼编码如上结论所述。
15.3-4
令$L(T)$表示满二叉树$T$的叶子节点的频率之和,$t(u)$表示以$u$为根节点的子树,$l(T),r(T)$分别为$T$的左子树和右子树。
题目相当于证明$\displaystyle{B(T)=\sum_{u\in T} L(l(t(u)))+L(r(t(u)))}$。对于$T$中的一个内部节点$u$而言,$L(l(t(u)))+L(r(t(u)))$相当于是以$u$为根节点的子树的叶节点频率之和,即$L(t(u))$;对于一个叶节点$l$,由于左右子节点都为空,因此$L(l(t(u)))+L(r(t(u)))$的值为$0$。
最终转化成证明$\displaystyle{B(T)=\sum_{u\in T} L(t(u))-L(T)}$。考虑使用归纳法证明这个式子。
对于一棵只有$1$个节点的二叉树$E$,注意$E$也是一个满二叉树,那么根据$B(E)$的定义,可知$B(E)=L(E)-L(E)=0$,原结论成立。
对于任意一棵$n$个节点的满二叉树树$T$,设其根节点为$x$。假设对于$1,2,3,\dots n-1$个节点的满二叉树,上面的结论都成立,那么其左子树$l(t(x))$和右子树$r(t(x))$都满足这个条件。由于$T$可以视为是由根节点$x$,左子树$l(t(x))$和右子树$r(t(x))$构成,因此相对于$T,l(t(x))$和$r(t(x))$无论是根节点还是叶子节点,深度都增加了$1$,因此总体代价都增加了$L(l(t(x)))+L(r(t(x)))=L(T)$。那么有
$\begin{aligned}
B(T)&=B(l(x))+B(r(x))+L(T)\
&= \sum{u\in l(x)} L(t(u))-L(l(x))+\sum{u\in r(x)} L(t(u))-L(r(x)) + L(T)\
&= \sum{u\in l(x)} L(t(u))+\sum{u\in r(x)} L(t(u))\
&= \sum{u\in T-{x}} L(t(u))\
&= \sum{u\in T} L(t(u))-L(T)\
\end{aligned}$
故原结论成立。
15.3-5
首先,每一个字符都需要用$\lceil\lg n\rceil$位二进制编码表示,最终需要$n\lceil\lg n\rceil$位的编码表示这个从编码映射到字符的字典。另外的$2n-1$个字符则用来表示这棵哈夫曼树的结构。这棵树一共有$2n-1$个节点,我们用$0$表示内部节点,用$1$来表示叶子节点,然后先序遍历整棵树,得到一个$2n-1$比特的序列。由于满二叉树每个内部节点必定有两个子节点,回溯到原来的节点再继续遍历时直接从另外一个子节点往下走即可,因此这个比特序列一定能够还原出原来的满二叉树结构。
具体的过程由REBUILD-CODING给出。
1 | // S是这个n ⌈lg n⌉ + 2n - 1长度的字符串;D表示编码规则的转换(可以抽象地理解成一个哈希表),将⌈lg n⌉位比特转换成一个字符。 |
15.3-6
先插入频率为$0$的哑节点,使得最小优先队列$Q$的大小模$3$的值为$1$。与构建二元哈夫曼树时的情况相同,每次取出$3$个节点,并且新生成一个节点$q$的频率是这$3$个节点的频率之和,并将$q$连接到这$3$个节点。具体过程由算法HUFFMAN3给出。
1 | HUFFMAN3(C) |
贪心选择性和最优子结构证明和二叉的情况几乎完全相同。最优子结构的证明过程是替换一个三叉节点,贪心选择性证明则考虑频率最小的$3$个节点和深度最深的$3$个节点。
15.3-7
令$n=8$,序列${f1,f_2,\dots,f{2^n}}$表示所有字符的频率之和,并且已经排好序。按照题目定义,还满足$2f1\ge f{2^n}$。
按照HUFFMAN算法,可以知道之后插入的新节点$F1=f_1+f_2\ge f{2^n}$,可以视为经过了$2^{n-1}-1$次FOR循环后,节点$F1$才有机会再弹出。那么在这$2^{n-1}$次迭代中,$f$的所有元素都将被第5-6行弹出了一次,并且构建成了一个长度为$F{2^{n-1}}$的有序序列。可以证明仍然满足$2F1\ge F{2^n-1}$,因为$2F1=2f_1+2f_2\ge f{2^{n}-1}+f{2^{n}}=F{2^{n-1}}$。
最终由于同一层下的节点每次都恰好被遍历,因此构建出来的哈夫曼树是一个满二叉树,每一个字符都用一个$n$比特编码来表示。因此在这种情况下,哈夫曼编码并不比定长编码有效。
15.3-8
可以知道,大小为$n$比特的不同文件一共有$2^n$。一个无损压缩器为了区分这$2^n$个文件,压缩产生的结果必定也是$2^n$个不同的编码。如果使用小于$n$比特的所有位串来区分这些文件,那么一共有$2^0+2^1+\dots+2^{n-1}=2^n-1$种方法,但是仍然还有$1$个文件不能用小于$n$比特的编码表示。因此不存在一个无损压缩器可以对所有文件,都能产生有效的压缩。