无损压缩
串表压缩
又称LZW(人名首字母连缀)压缩。
原理:提取原始文本文件数据中的不同字符,基于这些字符创建一个编译表,然后用编译表中的字符的索引来替代原始文本文件数据中的相应字符,减少原始数据大小。应该注意,编译表不是事先创建好的,而是根据原始文件数据动态创建的,解码时还要从已编码的数据中还原出原来的编译表。
Run Length Encoding
超简单,就是把“aaabbbbccd”转成“a3b4c2d1”。
适用于重复率高的。
最优二叉树
又称Huffman树。
目的:
令元素出现频率越大,对应“密码”越短,以达到压缩。
数学上,我们要最小化
f
(
t
)
:
=
∑
i
=
1
N
w
i
l
i
.
(1)
f(t):=\sum_{i=1}^N w_il_i.\tag{1}
f(t):=i=1∑Nwili.(1)
其中
l
i
l_i
li为第
i
i
i个元素的编码长度,
w
i
w_i
wi是其出现的频率,
t
t
t是树。
有限制条件 ∑ w i = 1 , l i ∈ Z + \sum w_i=1,l_i\in Z^+ ∑wi=1,li∈Z+。
同样的权值可以有不同的huffman方案(交换左右子就行)。(就是为了考试的屁话……)
算法:
我们考虑用二叉树来表示01编码,以边往左为0,往右为1。
为了不等长编码,所以字符编码不能有相同前缀,也就是只用二叉树的叶子节点。
首先,按照“权”(例如频率)将元素排为一列。
接着,拿走前两个元素(“权”最小的两个字符)。
再将它们标记为Huffman树的树叶,将这两个树叶标为一个分支结点的两个孩子,而该结点的权即为两树叶的权之和。
将所得节点放回序列。
重复上述步骤直至序列处理完毕。
证明:
正确性:
只有叶子节点存储了信息。
最优性:
断言:权值最小的两个结点,记为 a , b a,b a,b,在最优树 t 0 t_0 t0中一定是兄弟。
由 ( 1 ) (1) (1)可知, w i w_i wi越小,则 l i l_i li越大。不然,交换这一对元素在树上的位置, ( 1 ) (1) (1)会减小。这与此为最优树矛盾。
于是 a , b a,b a,b必为兄弟,因为它们的长度一样,且最大的 l l l必然成对(若 a a a为独子,就不需要这个父节点)。
现在把 a , b a,b a,b删除,将它们的父节点——记作 a & b a\&b a&b——的权值设为它们的和,得到新树 t ′ t' t′。
可知
f
(
t
0
)
=
f
(
t
′
)
+
a
.
w
+
b
.
w
.
f(t_0)=f(t')+a.w+b.w.
f(t0)=f(t′)+a.w+b.w.
因为
t
0
t_0
t0是最优树,所以
t
′
t'
t′一定是最优树。不然,把
t
′
t'
t′中的
a
&
b
a\&b
a&b展开为左右子结点,就得到比
f
(
t
0
)
f(t_0)
f(t0)更小的,矛盾。
复杂度:
时间: O ( n log n ) O(n\log n) O(nlogn)。
就是维护那个优先队列的时间复杂度。
推广:
k叉huffman树
-
用和二叉树一样的算法先建树。然后令i为1。
-
判断第i层非叶子结点是否含有k棵子树,如果是,则i++,执行(2);如果否,执行(3)。
-
从第i+2层选择权值最大的结点作为第i层结点的一颗子树。