哈夫曼树和哈夫曼编码
哈夫曼编码是属于一种不定长编码,我们首先先了解一下定长编码。
原字符串:“ A A A B B A C C C D E E A ”
根据定长编码规则我们可以将此字符串编码如下:
由此可以得到字符串编码后的二进制串为:
“000000000001001000010010010011100000”
此编码长度为:39
树的概念
树是一种非线性的数据结构,是若干节点的集合,下图就是一棵树。
节点:途中A,B,D等都是节点,不仅包含数据还包含指向其他节点的分支,A是根节点。
节点的度:节点拥有的分支个数。
叶子节点:度为0的节点,图中E,H,C,I都是叶子节点。
路径和路径长度:路径是指树中一个节点到另一个节点的分支所构成的路线,路径长度就是路径分支的数目。A-H路径长度为3.
二叉树
二叉树是节点的度≤2的树,且子树有左右之分,顺序不能颠倒。下图就是一颗二叉树。
带权路径长度:节点具有权值,从该节点到根之间的路径长度乘以节点的权值就是该节点的带权路径长度。A节点的带权路径长度:6 * 3 = 18。
树的带权路径长度(WPL):树中所有叶子节点的带权路径长度之和。
上图二叉树的WPL就是:6 * 3+11 * 2+3 * 3 = 49
哈夫曼树
原字符串:“ A A A B B A C C C D E E A ”
用哈夫曼树的方法解编程题的算法就叫做哈夫曼算法,那么如何得到哈夫曼树?
- 第1步:先将权值最小的D和B组成一颗树,这棵树的权值就是D和B两颗字树的权值之和就是3
- 第2步:再将剩下的里面最小的E和C组成一颗新树,新树的权值也就是E和C的权值之和为5
- 第3步:选择根节点权值最小的两棵树,也就是3和5组成新的树权值为8,
- 第4步:继续选择根节点权值最小的两棵树(5和8)来进行合并,得到的新树的权值为13, 至此我们就得到了一个哈夫曼树。
根据哈夫曼树我们可以得到哈夫曼编码如下:
根据上述编码规则,将原字符串生成二进制编码串如下:
"00010110101111111111001101100"
长度为:29 \
常规编码是定长编码,哈夫曼编码属于不定长编码。即使是不定长编码也不会产生歧义,因为哈夫曼编码是前缀码(前缀码:任意字符的编码串都不是另一个字符编码串的前缀)。哈夫曼编码不仅是前缀码,还是最短带权路径长度编码,这样计算机所存储的二进制就减少了,所以可以应用在各种压缩算法当中,而且是无损的。
当前市场的图片压缩方案
(1)质量压缩
核心代码:
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);
(2)像素压缩
核心代码:
BitmapFactory.Options newOpts = new BitmapFactory.Options();
newOpts.inSampleSize = be; // 设置采样率
newOpts.inPreferredConfig = Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeFile(imgPath, newOpts);
File thumbnailFile = getThumbnailFile(new File(imgPath));
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(thumbnailFile));"
(3)无损压缩
上面两种都是有损压缩,是在java层进行的图片压缩。哈夫曼压缩属于无损压缩是在native层实现的图片压缩。我们使用哈夫曼压缩是用到一个第三方库jpeg,jpeg是一个用于JPEG解码编码的库。libjpeg默认是包含哈夫曼压缩算法的,使用
jcs.optimize_coding = true; 就可以开启哈夫曼功能。
压缩效果如下: