Huffman二进制编码以及文本的压缩与解压

news2025/4/19 9:57:52

目录

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

Huffman树转化成二进制编码

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

   如果文本包含很多字符,那么从root节点到根节点的路径可能会很长。如果将二进制路径保存到int型变量中,路径长度超过32位那么一个int变量就不能完整保存路径。因此只能使用数组来保存路径,可以用byte,short,int,long。选择适中的int[]或者知道有很多字符时,可以选择long[]。还是要根据具体情况分析,下面采用long[]类型来保存路径。

在这里插入图片描述


     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");
         }

     }

	//将字符串转化成bits
     public 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;
     }


测试:


    @Test
    public 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: 0

b编码长度:2	 code: 1

a编码长度:3	 code: 5

c编码长度:2	 code: 3

e编码长度: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;
    }

  • 测试


    @Test
    public 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 = 72;比最原始的103要小一点。因为这段文本基本没有重复的,所以压缩效果不好。

文本解压

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

  • 代码

    public String decode(EncodeInfo info){
        List<List<Long>> encodes = info.encodes;
        HMNode root = info.root;
        int endListIndex = encodes.size()-1;
        int endElementIndex = encodes.get(endListIndex).size()-1;
        HMNode prev = null;
        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){
                    prev = parse(prev,codes.get(j),info.lastLength,info);
                }else{
                    prev = parse(prev,codes.get(j),BIT_CELL_LENGTH,info);
                }
            }
        }

        return null;
    }

    /**
     * @param prev
     * @param bits
     * @param len long型变量的有效bit长度;
     * @param info
     * @return
     */
    public HMNode parse(HMNode prev,long bits,int len,EncodeInfo info){
        long curBit = 0;
        while(prev != null && len > 0){
          curBit = (bits>>>(BIT_CELL_LENGTH-1));
          if(curBit == 1 ){
              prev = prev.right;
          }else{
              prev = prev.left;
          }
          bits<<=1;
          len--;
          if(prev.leaf){
              System.out.print(prev.str);
              if(len ==0)
                  return null;
              else
                break;
          }

        }
        if(len == 0)return prev;
        return parse(prev=info.root,bits,len,info);
    }
  • 测试

    @Test
    public 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产生的



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/109534.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTTP权威指南------URL与资源

目录 URL标准格式 URL快捷方式 动扩展URL 字符 方案详解 web基础中介绍了URI、URL与URN&#xff1b; URI是一类更通用的资源标识符&#xff0c;URL是它的一个子集&#xff1b; URI是一个通用的概念&#xff0c;它主要由URL与URN组成&#xff1b; URL是通过描述资源的位…

【Kafka】Linux下搭建kafka服务,完整学习案例

【Kafka】Linux下搭建kafka服务&#xff0c;完整学习代码案例&#xff08;一&#xff09;Kafka架构基础【1】图解kafka是什么&#xff1f;&#xff08;1&#xff09;为什么需要消息队列&#xff08;2&#xff09;Topic主题&#xff08;3&#xff09;分区&#xff08;4&#xff…

【UE4 第一人称射击游戏】06-设置动画角色2

步骤&#xff1a; 1.打开“WalkRun_BS”&#xff0c;将最左边中间的点和最右边中间的点的动画改为“walk_backward_inPlace” 2.打开“SWAT_AnimBP”&#xff0c;双击“Walk_Run” 双击“Walk_Run” 将混合空间“WalkRun_BS”拖入 将“Direction”和“Speed”提升为变量&#…

车载以太网 - 初识DoIP - 01

1、DoIP是什么? 从表达形式上 它就是UDS诊断套上车载以太网的马甲,然后实现UDS诊断的所有内容。通过下图DoIP的报文帧格式,从下面往上看,最尾部的数据我们能够很清晰的看到,User Data实际上就是UDS诊断数据,比如:10 03亦或是22 F1 86等信息。然后再往前就是源地址和目标…

Python通过Spleeter实现音唱人声(歌声)伴奏分离

程序示例精选 Python实现音唱人声(歌声)伴奏分离 如需安装运行环境或远程调试&#xff0c;见文章底部微信名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《Python实现音唱人声(歌声)伴奏分离》编写代码&#xff0c;功能包括了人声(歌声)-伴奏分离。运…

Dubbo、Spring Cloud和kubernetes该如何选型?

文章目录背景微服务的公共关注点微服务平台的选型横向比对微服务优劣比对背景 做技术选型。如果缺乏足够的经验&#xff0c;对so a啊&#xff0c;微服架构原理以及整个行业服务化演进的历史缺乏了解。 那么&#xff0c;对这个三个产品该如何选择啊&#xff1f;的确会感到困惑。…

想做副业怎么才能找到适合的项目,六条建议让你找副业不再迷茫

大家好&#xff0c;我是蝶衣王的小编 现在疫情反复&#xff0c;赚钱太难了。许多年轻人想发展副业。而现实情况往往是不知道做什么以及如何做&#xff0c;这是非常令人困惑的。我在这里分享六个要点&#xff0c;让你找到合适的项目。 六个步骤分别是 会观察、观察相关信息和赚…

有趣的网站分享——福音戰士標題生成器

说起文字标题生成器其实也是有一定的历史了。 他们往往源于某种媒介所带来的个性化标题的出现&#xff08;比如动画或电影、广告、产品Logo等&#xff09;。 被互联网群众察觉分享后在模因传播的现象下深入人心。 比如下面这样的&#xff1a; 就出自知名交友网站P…… 额咳。…

快速入门Spring MVC 一篇就够了

前言 我们前面学习了Spring两大核心机制IoC和AOP&#xff0c;接下来我们一起来学习Spring MVC。这篇文章带你快速入门Spring MVC。 Spring MVC概述 Spring MVC是目前主流的实现MVC设计模式的框架&#xff0c;是Spring框架的一个分支产品&#xff0c;以Spring loC容器为基础&…

Fabric.js 铅笔笔刷

本文简介 点赞 关注 收藏 学会了 fabric.js 的铅笔其实是继承基础画笔的一个工具&#xff0c;在基础画笔的基础上多了“拐角平滑度”等配置项。 本文讲解铅笔的基础用法以及常用事件。 常规配置 真实世界的铅笔有不同的型号&#xff0c;颜色的深浅、笔芯的硬度都是不同的…

90后,27岁转行软件测试,从月入3000+到月薪过万,打开了人生新篇章~

承蒙时光不弃&#xff0c;感谢努力的自己。以前总是在某些鸡汤文中看到这句话&#xff0c;当时觉得过于矫情&#xff0c;而如今当我突破重重困难成功转行&#xff0c;收获了更好的人生后&#xff0c;才发自内心的也有了这样的感叹。 几个月的努力和辛劳&#xff0c;一时之间难以…

转互联网好难,如何避免无效转行?

如果你现在是在传统行业工作&#xff0c;想转行互联网&#xff0c;应该怎么做呢&#xff1f; 很多人经常会担心自己的行业、专业、年龄等等会是障碍&#xff0c;或者自己没有经验&#xff0c;去面试的时候公司却都需要相关经验的人&#xff0c;怎么办呢&#xff1f; 这篇文章…

图形驱动软件栈

图形驱动软件栈 HINZER&#xff0c;2022年&#xff0c;我在北京。芯片设计行业&#xff0c;GPU 固件和驱动开发&#xff0c;对嵌入式系统感兴趣。 1 说明背景 1.1 近来想法 做了一段时间的 GPU 固件和驱动开发&#xff0c;加上平时学习的一些零散的知识&#xff0c;最近打算整…

【Web前端HTML5CSS3】06、盒模型

六、盒模型 1、文档流&#xff08;normalflow&#xff09; 网页是一个多层的结构&#xff0c;一层摁着一层 通过 CSS 可以分别为每一层来设置样式&#xff0c;作为用户来讲只能看到最顶上一层 这些层中&#xff0c;最底下的一层称为文档流 文档流是网页的基础我们所创建的元…

牛客java刷题知识点总结(八)

方法调用 类中变量&#xff1a; 除了private权限外&#xff0c;其他权限的变量&#xff08;没有表示默认default&#xff09;&#xff0c;均可以用“对象.变量名”来调用。对于private变量&#xff0c;即使使用static&#xff0c;也不能用“类.变量名”来调用私有变量。只能通过…

UID走私:一种在线跟踪用户的新技术

©网络研究院 几十年来&#xff0c;广告商和网络追踪者已经能够在用户访问的所有网站上汇总用户信息&#xff0c;主要是通过在用户的浏览器中放置第三方cookies。 两年前&#xff0c;几个优先考虑用户隐私的浏览器开始默认屏蔽所有用户的第三方cookies。 对于那些代表其…

2022年转行编程选哪门语言?这份报告给你答案!

本报告展示了 JetBrains &#xff08;IntelliJ IDEA的开发公司&#xff09;于2021年进行的第五次年度开发者生态系统调查的综合结果。来自 183 个国家或地区的 31,743 名开发者帮助我们绘制了开发者社区版图。 本文将摘录部分个人觉得有价值的报告&#xff0c;从全球视角以国内…

AI-多模态-2021:Stable Diffusion【根据文本生成图像】【开源】【目前开源模型中最强】

最近大火的Stable Diffusion也开源了(20220823); 我也本地化测试了一下效果确实比Dall-E mini强太多了&#xff0c;对于我们这些玩不上Dall-E2的这个简直就是就是捡钱的感觉&#xff0c;当然后期跑起来&#xff0c;稍微不注意显存就炸了。 这里我写一下安装过程&#xff0c;具…

Fabric.js 限制边框宽度缩放

本文简介 点赞 关注 收藏 学会了 使用 fabric.js 在某些情况下你可能需要固定元素边框的宽度&#xff0c;仔细看文档你会发现 fabric.js 已经为我们提供了这个功能。本文简单介绍一下这个功能。 实现方法 先揭晓答案&#xff0c;将元素的 strokeUniform 属性设置为 true 后…

适合贵校的核心营销讯息

香港 (Xinwengao.com) — 优秀的学校具备策略来提升形象、提高学生的成功率、改善收生&#xff08;和重新入学&#xff09;的成果。这包括为你的学校定下营销讯息。大多数学校都具备自己的营销策略&#xff0c;但很少有营销讯息。一个有力的营销讯息可大大地支援营销策略。 即…