LZW编码的学习与实现

LZW编码的学习与实现Preface 看了一天 感觉终于搞明白了一点 代码终于写对了 编码过程大四的时候学过一点点 按部就班的按照步骤来做就行了 解码过程貌似课堂上老师没讲 自己看 wiki 上的讲解和 example 搞懂了 LZW 全称 Lempel Ziv Welch 就是这个三个人搞出来的 LZW 的工作思路 考虑一段数据 abcabcabc 对于这样的一段数据 如果不做任何处理和压缩 假设每个字符用一个字节来表

这里写图片描述

大家好,现在是2020年5月19日,不知不觉5年过去了,看到最近还有很多人评论我的这篇文章,非常的开心和满足,显然我写这篇文章所做的微小贡献帮助到了一些人。今天我从头到尾再看了一遍我写的东西,还是有一些东西没有描述清楚,比如下面第一个表里里面,dictionary 那一列里面的 xx:5,我就一直没明白什么意思。另外,一些朋友在评论区对256,257是怎么来的感到疑惑,我都会对文章做一次修订,将这些内容描述清楚,谢谢大家。

Preface

看了一天,感觉终于搞明白了一点(代码终于写对了),编码过程大四的时候学过一点点,按部就班的按照步骤来做就行了,解码过程貌似课堂上老师没讲,自己看wiki上的讲解和example搞懂了。

LZW全称Lempel–Ziv–Welch,就是这个三个人搞出来的。

LZW的工作思路,考虑一段数据,abcabcabc,对于这样的一段数据,如果不做任何处理和压缩,假设每个字符用一个字节来表示,直接存储的空间应该是9字节。详细说明下:
下表是我们dictionary,也就是码表,字符与编码的对应。

character decimal binary
a 1 0000 0001
b 2 0000 0010
c 3 0000 0011

观察上面的字符串,发现abc是重复的,如果abc能用一个字节来表示的话,那么,只需要3字节存储就足够了。

character decimal binary
abc 1 0000 0001

OK,LZW就是这个尿性,它就是想这么搞。

Encoding

基本思想

编码的基本思想是这样的,首先我们有一个码表或者称为dictionary,里面只定义单个字符和编码,比如0-9a-zA-Z这些,其实ASCII码中的所有全部都放进去都可以。

也就是说通常用十进制数0-255来表示单一字符(single character)。

然后我们开始读取字符串,很显然对于任何常规字符串,每个single character都可以得到一个码字,但是如果我们不做处理的话,那么n个字符encode后,就是n个码字了完全没有压缩。

所以,再边读取的时候,一边开始对dictionary进行扩增,不断的将single character拼接成dictionary中不存在的符号(symbol),扩充dictionary,这样下次再次遇到这样的组合,就可以使用有一个码字表示了。

流程

P -> Previous -> 表示之前的字符 C -> Current -> 表示当前读取的字符 

然后我们考虑字符串abcbcabcabcd

来,按步骤演算一下:

首先我们需要一个dictionary,简单考虑,只考虑a-d,4个英文小写字母吧。这里想表达的意思是,整个宇宙全世界只有a-d 4个英文字母,我们的ascii表中也只有 4 个字母。

symbol decimal
a 1
b 2
c 3
d 4

step P C is PC in dic output P dictionary(符号:编码) description
1 NULL a 初始化,不处理
2 a b no 1 ab:5 ab不存在dictionary中,扩充
3 b c no 2 bc:6
4 c b no 3 cb:7
5 b c yes bc在dictionary中,P = PC
6 bc a no 6 bca:8
7 a b yes
8 ab c no 5 abc:9
9 c a no 3 ca:10
10 a b yes
11 ab c yes
12 abc d no 9
13 d NULL 4 结束

经过上面的演算过程,我们的dictionary也扩展为:

symbol decimal
a 1
b 2
c 3
d 4
ab 5
bc 6
cb 7
bca 8
abc 9
ca 10

代码实现

最后贴下自己实现的代码:

 public static List<Integer> encode(String data) { 
         List<Integer> result = new ArrayList<>(); //初始化dictionary int idleCode = 256; HashMap<String, Integer> dic = new HashMap<>(); for (int i = 0; i < 256; i++) { 
         dic.put((char)i + "", i); } String previous = ""; String pc = ""; for (char c : data.toCharArray()) { 
         pc = previous + c; if (dic.containsKey(pc)) { 
         previous = pc; } else { 
         dic.put(pc, idleCode++); result.add(dic.get(previous)); previous = "" + c; } } //最后一次输出 if (!previous.equals("")) { 
         result.add(dic.get(previous)); } return result; } 

Output

//abcbcabcabcd [97, 98, 99, 257, 256, 99, 260, 100] 
256, 257 是怎么来的?

上面用表格模拟整个过程的时候,是假设全世界只有4个字符,ASCII码表的大小也只有4个字符,但是上面这会程序运行时,我们的世界又是真实世界了,ASCII表示有256个字符的,编码范围从0到255。那么我们新创造的字符用什么来编码呢?当然是从 255 后面开始追加了。

Decoding

主要思想

解码的主要思想是,一个编码序列,我们也是事先知道一个固定的码表,我一边读取码字,一边输出解码后的字符,同时一边扩充码表。这个我也觉得好难用用文字表达清楚,上流程图。

流程

这里写图片描述
同样的,这里给出一个编码序列

[97, 98, 99, 257, 256, 99, 260, 100] 

首先我们需要一个默认的dictionary。

decimal symbol
97 a
98 b
99 c
100 d

接下来是演算过程:

Input P C is C in dic output dictionary description
97 NULL a yes a
98 a b no 1 ab:5 ab不存在dictionary中,扩充
99 b c no 2 bc:6
257 c b no 3 cb:7
256 b c yes bc在dictionary中,P = PC
99 bc a no 6 bca:8
260 a b yes
100 ab c no 5 abc:9

代码实现

 public static String decode(List 
        
          arr) { StringBuilder result = new StringBuilder(); int idleCode = 256; HashMap 
         
           dic = new HashMap<>(); for (int i = 0; i < 256; i++) { dic.put(i, (char)i + ""); } String p = ""; String c = ""; for (int code : arr) { if (dic.containsKey(code)) { c = dic.get(code); } else if (code == idleCode) { //这里唯一的情况是 aaaaaaa c = c + c.charAt(0); } else { System.out.println("bad encode"); } if (!p.equals("")) { dic.put(idleCode++, p + c.charAt(0)); } result.append(c); p = c; } return result.toString(); } 
          
        

Summary

References

  1. Lempel–Ziv–Welch from Wikipedia.
  2. LZW的各种语言的实现
  3. ASCII
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/210618.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 上午7:15
下一篇 2026年3月19日 上午7:15


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号