目录

  • Huffman树转化成二进制编码
  • 文本压缩
  • 文本解压

Huffman树转化成二进制编码

   在上一篇博客的末尾,将Huffman树转化成了01 构成的字符串,显然在实际应用中不是这种操作。我们实际想要的是01构成的一串bits;举个例子:字符"A" 编码:0100 0001(8bit),假设我们重新编码之后字符 "A" 的路径是01,只占2个bit,应该用 01(bit)表示,而不是字符类型的"01",如果用字符类型的01来重新编码,那经过Huffman编码之后的数据比原本的数据还大。因此需要改进一下。(ps:如果不太清楚Huffman树的原理,可以先看上一篇博客)

   如果文本包含很多字符,那么从root节点到叶子节点的路径可能会很长。如果将二进制路径保存到int型变量中,路径长度超过32位那么一个int变量就不能完整保存路径。因此只能使用数组来保存路径,可以用byte,short,int,long。选择适中的int[]或者其他类型。最好使用byte[]这是最节省内存的,但本文采用long[]类型来保存路径。(完整代码地址)

//统计字符使用频率和构建Huffman树的代码在上一篇文章中贴出来了,不再重复粘贴;完整代码在链接中;public void bitCode(HMNode root,Map<Character,long[]> huffmanCode,String code){if(root.leaf){System.out.println(root.str+ " -> " + code);huffmanCode.put(root.str,convertBits(code));return;}if(root.left != null){bitCode(root.left,huffmanCode,code+"0");}if(root.right != null){bitCode(root.right,huffmanCode,code+"1");}}//将字符串转化成bitspublic long[] convertBits(String code){int len = code.length() ;int codeLength = len% 64 ==0 ? len/64:len/64+1;int index = 1;int offset = 63;long[] bitCodes = new long[codeLength+1];char[] codes = code.toCharArray();for (int i = 0; i < codes.length; i++) {long bitCode = bitCodes[index];long c = codes[i]-'0';bitCode |= (c<<offset);bitCodes[index]=bitCode;if(offset== 0){offset = 63;index += 1;}else{offset -= 1;}}bitCodes[0]=(long)len;//第一个数字是用来记录编码的长度;return bitCodes;}

测试:

@Testpublic void test(){String text = "aaabbbbbccccccddddee";HMNode root =buildHuffManTree(text);Map<Character,long[]>code = new HashMap<>();bitCode(root,code,"");System.out.println("+++++++++++++++++++++++++++++++++++++++++++++");code.forEach((key,val)->{long codeLen = val[0];System.out.print(key+"编码长度:"+codeLen +"\tcode: ");for (int i = 1; i < val.length-1; i++) {System.out.print(" "+val[i]);}System.out.print(" "+(val[val.length-1]>>>(64-codeLen)));System.out.println("\n");});}=========================================res=================================================
d -> 00
c -> 11
b -> 01
e -> 100
a -> 101
+++++++++++++++++++++++++++++++++++++++++++++
d编码长度:2   code: 0b编码长度:2   code: 1a编码长度:3   code: 5c编码长度:2   code: 3e编码长度:3   code: 4

文本压缩

  EncodeInfo是保存压缩后的文本,设置成了一个二维数组。(现在想想其实没必要,因为必定是一边压缩一边保存到磁盘的。不可能把数据完全压缩之后再保存。)

  遍历文本将每一个字符的Huffman二进制码数组按顺序保存到压缩文本的二维数组中,这个需要稍微熟悉下位运算。(保存到二位数组就没有必要,之后再改吧)

   class EncodeInfo{List<List<Long>> encodes = new ArrayList<>();int lastLength = 0;//encodes最后一个list的最后一个long元素中,有效bit个数HMNode root;public EncodeInfo(HMNode root){List<Long> code = new ArrayList<>(ARRAY_SIZE);code.add(0L);encodes.add(code);this.root = root;}}static int ARRAY_SIZE = 1024;static int BIT_CELL_LENGTH = 64;public void encode(String text,EncodeInfo info){Map<Character,long[]>code = new HashMap<>();bitCode(info.root,code,"");for (char c : text.toCharArray()) {addBitCode(info,code.get(c));}}private void addBitCode(EncodeInfo info ,long[] bits){List<List<Long>> encodes = info.encodes;List<Long> codes = encodes.get(encodes.size()-1);//获取到最后一个List;//得到二进制码数组最后一个元素的有效bit数;long last = bits[0]%BIT_CELL_LENGTH == 0 ? BIT_CELL_LENGTH : bits[0] % BIT_CELL_LENGTH;for (int i = 1; i < bits.length-1; i++) {long b = bits[i];int size = codes.size();long val = codes.get(size-1) ;val |= (b >>> info.lastLength);//前半部分codes.set(size-1,val);long after = b << (BIT_CELL_LENGTH-info.lastLength);//后半部分codes = addLast(info,after);info.lastLength = info.lastLength == 0 ? 0: BIT_CELL_LENGTH-info.lastLength;}long lastBits = bits[bits.length-1];long val = codes.get(codes.size()-1);val |= (lastBits >>> info.lastLength);codes.set(codes.size()-1,val);if(last + info.lastLength < BIT_CELL_LENGTH){info.lastLength += last;}else if(last + info.lastLength == BIT_CELL_LENGTH){info.lastLength =0;addLast(info,0L);}else{lastBits<<= (BIT_CELL_LENGTH-info.lastLength);addLast(info,lastBits);info.lastLength +=(last-BIT_CELL_LENGTH);}}private List<Long> addLast(EncodeInfo info,long val){List<List<Long>> encodes = info.encodes;List<Long> codes = encodes.get(encodes.size() - 1);int size = codes.size();if(size == ARRAY_SIZE){codes = new ArrayList<>(ARRAY_SIZE);encodes.add(codes);}codes.add(val);return codes;}
  • 测试
@Testpublic void testEncode(){String text = " sfsdftechfdfhgjhtghrytthnhGGHMBDV打发士大夫SDCYUGERFASDSDW,.*&^%$#[]_+{};;;;;,.XCCCCC地方法规";int beforeEncode = text.getBytes().length;System.out.println("编码前:beforeEncode:"+beforeEncode +";");HMNode root =buildHuffManTree(text);EncodeInfo info =  new EncodeInfo(root);encode(text,info);info.encodes.forEach(x->{for (Long aLong : x) {System.out.println(aLong);}});System.out.println("最后一个long的有效bit个数:"+info.lastLength);}
  • 结果
编码前:beforeEncode:103;
* -> 000000
夫 -> 000001
+ -> 000010
地 -> 000011
方 -> 000100
A -> 000101
B -> 000110
规 -> 000111
E -> 001000
F -> 001001
H -> 001010
M -> 001011
发 -> 001100
R -> 001101
打 -> 001110
U -> 001111
法 -> 010000
V -> 010001
W -> 010010
X -> 010011
Y -> 010100
[ -> 010101
] -> 010110
^ -> 010111
_ -> 011000
c -> 011001
e -> 011010
j -> 011011
士 -> 011100
n -> 011101
r -> 011110
y -> 011111
; -> 1000
{ -> 100100
} -> 100101
G -> 10011
C -> 1010
h -> 1011
S -> 11000
D -> 11001
f -> 11010
t -> 11011
, -> 111000
. -> 111001
d -> 111010
g -> 111011
s -> 111100-> 1111010
# -> 1111011
$ -> 1111100
% -> 1111101
& -> 1111110
大 -> 1111111
-727686570736772137
6538905317935103933
-2614557749356706589
2089122890596168205
2620082441562243324
-4620800053492013930
2459590903680641548
4901886719416074240
最后一个long的有效bit个数:16

   数字就是文本经过Huffman编码压缩之后保存的信息;每个long保存8byte信息;可以计算出来被压缩之后的byte数:8 * 7 + 16 /8= 58;比最原始的103要小一点。因为这段文本基本没有重复的,所以压缩效果不好。

文本解压

  在解压时按顺序读每一个bit位,对照Huffman树,如果是1就走右分支,如果是 0就走左分支。(这个还是要看之前怎么编码的,如果之前在构建Huffman树时,1表示左 ;0表示右那就按对应的方式解析)找到叶子节点就输出字符,然后继续顺序读读取bit知道将所有bit读完为止。

  • 代码
public String decode(EncodeInfo info){List<List<Long>> encodes = info.encodes;int endListIndex = encodes.size()-1;int endElementIndex = encodes.get(endListIndex).size()-1;HMNode cur= info.root;for (int i = 0; i < encodes.size(); i++) {List<Long> codes = encodes.get(i);for (int j = 0; j < codes.size(); j++) {if(i == endListIndex && j == endElementIndex){cur= parse(cur,codes.get(j),info.lastLength,info);}else{cur= parse(cur,codes.get(j),BIT_CELL_LENGTH,info);}}}return null;}/*** @param cur* @param bits* @param len long型变量的有效bit长度;* @param info* @return*/public HMNode parse(HMNode cur,long bits,int len,EncodeInfo info){long curBit = 0;while(prev != null && len > 0){curBit = (bits>>>(BIT_CELL_LENGTH-1));if(curBit == 1 ){cur= cur.right;}else{cur= cur.left;}bits<<=1;len--;if(cur.leaf){System.out.print(cur.str);if(len ==0)return null;elsebreak;}}if(len == 0)return cur;return parse(cur=info.root,bits,len,info);}
  • 测试
@Testpublic void testDecode(){String text = "急急急uu  ,.;/.'*&^%$广东福建的好的的德国费尔法的的发夫算法发生过和323434234123吧vDVD产生的";int beforeEncode = text.getBytes().length;System.out.println("编码前:beforeEncode:"+beforeEncode +";");HMNode root =buildHuffManTree(text);EncodeInfo info =  new EncodeInfo(root);encode(text,info);decode(info);System.out.println("\n+++++++++++++++");System.out.println(text);}
  • 结果

编码前:beforeEncode:121;
的 -> 000
2 -> 0010
4 -> 0011
广 -> 01000
D -> 01001
发 -> 01010
法 -> 01011
生 -> 01100-> 01101
. -> 01110
u -> 01111
过 -> 100000
和 -> 100001
福 -> 100010
尔 -> 100011
V -> 100100
算 -> 100101
东 -> 100110
^ -> 100111
$ -> 101000
% -> 101001
& -> 101010
' -> 101011
吧 -> 101100
产 -> 101101
* -> 101110
夫 -> 101111
, -> 110000
/ -> 110001
1 -> 110010
v -> 110011
德 -> 110100
费 -> 110101
建 -> 110110
; -> 110111
3 -> 1110
好 -> 111100
国 -> 111101
急 -> 11111
急急急uu  ,.;/.'*&^%$广东福建的好的的德国费尔法的的发夫算法发生过和323434234123吧vDVD产生的
+++++++++++++++
急急急uu  ,.;/.'*&^%$广东福建的好的的德国费尔法的的发夫算法发生过和323434234123吧vDVD产生的

Huffman二进制编码以及文本的压缩与解压相关推荐

  1. 利用huffman编码对文本文件进行压缩与解压(java实现)

    利用huffman编码对文本文件进行压缩与解压 输入:一个文本文件 输出:压缩后的文件 算法过程: (1)统计文本文件中每个字符的使用频度 (2)构造huffman编码 (3)以二进制流形式压缩文件 ...

  2. huffman算法实现文件的压缩与解压

            本文采用哈夫曼编码的方式进行文件的压缩和解压缩,主要原理是通过huffman编码来表示字符,出现次数多的编码短,出现次数少的编码长,这样整体而言,所需的总的bit位是减少的.但是当大部 ...

  3. 利用huffman编码对文本文件进行压缩与解压 Java

    exe4j的使用教程可以看这篇博客:https://blog.csdn.net/zzzgd_666/article/details/80756430 目录 一.问题描述 二.要求 三.提示 四.运行环 ...

  4. Huffman编码、Shannon编码、Fano编码——《小王子》文本压缩与解压

    一.实验要求: 1 采用熵编码对<小王子>文本进行压缩,生成压缩文件: 2 将压缩文件解压,并与源文件比较: 3 从香农编码.Huffman编码.Fano编码中选择一种: 4 计算编码效率 ...

  5. 【简便解法】1078 字符串压缩与解压 (20分)_42行代码AC

    立志用更少的代码做更高效的表达 Pat乙级最优化代码+题解+分析汇总-->传送门 文本压缩有很多种方法,这里我们只考虑最简单的一种:把由相同字符组成的一个连续的片段用这个字符和片段中含有这个字符 ...

  6. 1078 字符串压缩与解压 (20 分)

    文本压缩有很多种方法,这里我们只考虑最简单的一种:把由相同字符组成的一个连续的片段用这个字符和片段中含有这个字符的个数来表示.例如 ccccc 就用 5c 来表示.如果字符没有重复,就原样输出.例如  ...

  7. 基于哈夫曼编码完成的文件压缩及解压

    这几天在较为认真的研究基于哈夫曼编码的文件压缩及解压,费了点时间,在这分享一下: 这里用链式结构,非顺序表结构: 文件压缩: 1.获取文件信息(这里采用TXT格式文本): 2.压缩文件: 3.写配置文 ...

  8. C++学习之路 | PTA乙级—— 1078 字符串压缩与解压 (20 分)(精简)

    1078 字符串压缩与解压 (20 分) 文本压缩有很多种方法,这里我们只考虑最简单的一种:把由相同字符组成的一个连续的片段用这个字符和片段中含有这个字符的个数来表示.例如 ccccc 就用 5c 来 ...

  9. 1078. 字符串压缩与解压 (20)-PAT乙级真题

    文本压缩有很多种方法,这里我们只考虑最简单的一种:把由相同字符组成的一个连续的片段用这个字符和片段中含有这个字符的个数来表示.例如 ccccc 就用 5c 来表示.如果字符没有重复,就原样输出.例如 ...

最新文章

  1. 《OpenCV3编程入门》学习笔记7 图像变换(三 )重映射
  2. Linux的目录结构和头文件相关;哪里找- sys/types.h, sys/stat.h
  3. html input image 尺寸,HTML DOM Input Image 对象
  4. Linux/windows下nginx的安装及使用
  5. linux下mqm用户下S开头日志,Linux新建用户,切换后只显示$问题
  6. 关于联合利华:我的第一次正式实习的单位!撒花!
  7. datax参数设置_DataX Web数据增量同步配置说明
  8. iOS:ASIHttpRequest虽不更新,但仍值得详细了解
  9. 基于ASP.NET AJAX技术开发在线RSS阅读器(下篇)
  10. 『科学计算_理论』矩阵求导
  11. 中南大学07 MATLAB中字符串的处理
  12. 使用U盘升级到win10系统
  13. html获取百度网盘 文件列表,教你如何导出百度网盘详细目录结构
  14. 倪海厦天纪笔记16_倪海厦-天纪16
  15. apache 启动失败错误排查
  16. Guarded Suspension模式:等待唤醒机制
  17. 新年PHP微信抽奖系统源码开源二开版带完整后台,支持修改开发
  18. springboot At least one base package must be specified的解决方式
  19. 传奇单机架设超详细图文教程
  20. UVA1626 Brackets sequence

热门文章

  1. 谭浩强 老师C语言第五版答案第三章
  2. 聊聊php脚本执行流程
  3. GB/T 8323.2塑料 烟生成 第2 部分:单室法测定烟密度试验方
  4. 【程序化天空盒】过程记录01:日月 天空渐变 大气散射
  5. iOS截取长图,自定义截取size
  6. android如何获取分屏区域的宽高,Android N获取屏幕高度的问题
  7. ICP、ISP、IAP、JTAG、SWD下载方式
  8. ctf古典密码从0到1
  9. OpenWRT飞行计划
  10. STM32 --- 使用内部FLASH存储数据