本文系转载+改编:http://blog.csdn.net/krossford/article/details/49157531
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
Encoding
基本思想
编码的基本思想是这样的,首先我们有一个码表或者称为dictionary,里面只定义单个字符和编码,比如0-9a-zA-Z这些,其实ASCII码中的所有全部都放进去都可以。
也就是说通常用十进制数0-255来表示单一字符(single character)。
然后我们开始读取字符串,很显然对于任何常规字符串,每个single character都可以得到一个码字,但是如果我们不做处理的话,那么n个字符encode后,就是n个码字了完全没有压缩。
所以,再边读取的时候,一边开始对dictionary进行扩增,不断的将single character拼接成dictionary中不存在的符号(symbol),扩充dictionary,这样下次再次遇到这样的组合,就可以使用有一个码字表示了。
流程
说实话,用纯文字描述还真的不是很好描述,于是我画了一个流程图来描述这样一个过程。
这里写图片描述
就是上面这个图了,代码只要照着这个写就能搞定问题。但出于尊重,还是要讲解下比较好。
首先我们需要两个暂存器,P和C,为啥叫这个名字呢。(当时上学那会,老师给的是p和s,完全不知道意义)
P -> Previous -> 表示之前的字符
C -> Current -> 表示当前读取的字符
1
2
然后我们考虑字符串abcbcabcabcd
来,按步骤演算一下:
首先我们需要一个dictionary,简单考虑,只考虑a-d,4个英文小写字母吧。
symbol decimal
a 1
b 2
c 3
d 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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Output
//abcbcabcabcd
[97, 98, 99, 257, 256, 99, 260, 100]
1
2
3
Decoding
主要思想
解码的主要思想是,一个编码序列,我们也是事先知道一个固定的码表,我一边读取码字,一边输出解码后的字符,同时一边扩充码表。这个我也觉得好难用用文字表达清楚,上流程图。
流程
这里写图片描述
同样的,这里给出一个编码序列
[97, 98, 99, 257, 256, 99, 260, 100]
1
首先我们需要一个默认的dictionary。
decimal symbol
97 a
98 b
99 c
100 d
接下来是演算过程:
代码实现
public static String decode(List<Integer> arr) {
StringBuilder result = new StringBuilder();
int idleCode = 256;
HashMap<Integer, String> 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();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Summary
References
Lempel–Ziv–Welch from Wikipedia.
LZW的各种语言的实现
http://rosettacode.org/wiki/LZW_compression
https://zh.wikipedia.org/wiki/ASCII