winRAR真难用,我决定自创一个(元婴期) 压缩

  元婴期简介

元婴期主要阐述的是压缩思路压缩的代码实现与分析

提供完整代码,代码在筑基期中     

博主空间https://blog.csdn.net/JOElib?spm=1011.2266.3001.5343筑基期https://blog.csdn.net/JOElib/article/details/123848806?spm=1001.2014.3001.5501结丹期https://blog.csdn.net/JOElib/article/details/123905069?spm=1001.2014.3001.5501


目录

🐼压缩思路

🐼压缩的代码实现与分析 

1.创建一个节点类🐻

代码分析:🐨

2.创建一个哈夫曼代码类🐻

代码分析:🐨

3.创建一个获取集合List的方法🐻

代码分析:🐨

4.建立一个创建哈夫曼树的方法🐻 

代码分析:🐨

5.创建一个获取哈夫曼编码的方法🐻 

代码分析:🐨 

 6.创建一个压缩的方法🐻

代码分析:🐨  

7.按步骤封装代码🐻 

🐼结论


 

🐼压缩思路

  1.  获取字符串的byte数组
  2.  将byte数组里面的元素及其出现的个数记录在Map集合中
  3.  将Map集合元素放入List集合中,根据List集合创建对应的哈夫曼树
  4.  利用哈夫曼树创建对应的哈夫曼编码,并放入另一个Map集合中
  5.  根据哈夫曼编码表取出元素,并进行截断进行最后的总压缩
  6.  封装代码

字符串 -> 哈夫曼树 -> 哈夫曼编码 -> 压缩 


🐼压缩的代码实现与分析 

1.创建一个节点类🐻

public class Node implements Comparable<Node> {
    public Byte ascii;
    public int value;
    public Node left;
    public Node right;

    public Node(Byte ascii,int value) {
        this.ascii = ascii;
        this.value = value;
    }

    public String toString() {
        return "Node[ascii = " + ascii + " value = " + value + "]";
    }

    public int compareTo(Node node) {
        return value - node.value;
    }

}

代码分析:🐨

  1. Byte是byte的包装类,不能写byte,后面有妙用,对应的是ascii,存放字符的ascii码
  2. value是权,存放着某个字符串出现的个数 
  3. compareTo方法是为了排序创造哈夫曼树,toString是为了方便输出校验程序是否出错,构造方法是为了初始化

2.创建一个哈夫曼代码类🐻

public class HuffmanCode implements Serializable {
    @Serial
    private static final long serialVersionUID = 4420;
    private static final HashMap<Byte,String> HUFFMAN_CODES = new HashMap<>();
    private static final StringBuffer STRING_BUFFER = new StringBuffer();
    private static int lastCount;
}

代码分析:🐨

  1. HUFFMAN_CODES这个常量是哈夫曼编码表,每个元素对应的哈夫曼编码都存放于里面,泛型为Byte(元素类型)和String(哈夫曼编码)
  2. STRING_BUFFER起辅助作用,用于辅助哈夫曼编码的完成
  3. 剩余属性都是解压所用,解压时再描述 

3.创建一个获取集合List的方法🐻

public static List<Node> getNodes(byte[] content) {
        var map = new HashMap<Byte,Integer>();
        var list = new ArrayList<Node>();
        for (var item : content) {
            var count = map.get(item);
            if (count == null) {
                map.put(item,1);
            }else {
                map.put(item,++count);
            }
        }   
        for (var entry : map.entrySet()) {
            list.add(new Node(entry.getKey(),entry.getValue()));
        }
        return list;
    }

代码分析:🐨

  1. 该方法的参数列表是byte[]数组,该byte[]数组是所需要压缩的字符串对应的byte[]数组,可以通过getBytes()这个方法获取
  2. new 一个Map集合,用于存放元素及其元素个数(相当于存放字符串里的字符及其字符个数),泛型分别指定为Byte(元素类型),Integer(元素个数)
    1. 注意:这里是遵循着变长编码的思路方式,不清楚可跳转至"筑基期"
  3. new一个List集合,用于创建哈夫曼树
    1. 注意:这里的原理如果不懂,可以跳转至"结丹期"
  4. 用增强for循环遍历byte[]数组
    1. 首先,我们先建立一个变量var来接受get()方法的返回值
    2. 如果get()方法返回值为null(Integer),说明该元素还没有被记录在Map集合中,所以调用put()方法,并指定个数为1
    3. 如果get()方法返回值不为null,说明该元素已经在Map集合中记录了,所以调用put方法,并指定个数为++count,表示到此刻为止,一共有++count个这样的元素

4.建立一个创建哈夫曼树的方法🐻 

public static Node createHuffmanTree(List<Node> list) {
        while (list.size() > 1) {
            Collections.sort(list);
            var leftNode = list.get(0);
            var rightNode = list.get(1);
            var parentNode = new Node(null, leftNode.value + rightNode.value);
            parentNode.left = leftNode;
            parentNode.right = rightNode;
            list.add(parentNode);
            list.remove(1);
            list.remove(0);
        }
        return list.get(0);
    }

代码分析:🐨

  1. 注意:叶子节点才是我们存放数据的节点,所以创建新子树时,根节点的ascii赋值为null
  2. 其他略,不清楚请跳转"结丹期"

5.创建一个获取哈夫曼编码的方法🐻 

public static void getCodes(Node node,String code,StringBuffer strBuf) {
        var curStrBuf = new StringBuffer(strBuf);
        curStrBuf.append(code);
        if (node != null) {
            if (node.ascii == null) {
                getCodes(node.left,"0",curStrBuf);
                getCodes(node.right,"1",curStrBuf);
            }else {
                HUFFMAN_CODES.put(node.ascii,curStrBuf.toString());
            }
        }
    }

代码分析:🐨 

  1. 参数列表:
    1. node表示从哪一个节点开始,所以我们传入一个根节点即可
    2. code表示编码规则,我们有以下规则
      1. 如果向左子树走,我们就传"0"
      2. 如果向右子树走,我们就传"1"
    3. strBuf用于字符串拼接,从而获取某个元素最终的哈夫曼编码
  2. 首先:先new一个StringBuffer(cuStrBuf)
    1. 原因:如果我们不去new一个StringBuffer而直接往参数列表传过来的strBuf直接改动,会导致strBuf对应的字符串永远的在增长,没有回溯的过程,导致一个元素的哈夫曼编码串联在另一个元素上
  3. 字符串拼接,根据code拼接
  4. 如果当前节点不为空,说明还有机会去递归找到所需要的节点
    1. 如果当前节点的ascii为null,说明时非叶子节点,不是所需要的元素,进行递归编码
    2. 向左递归,赋"0",并将curStrBuf传过去
    3. 向有递归,赋"1",并将curStrBuf传过去
  5. 否则时叶子节点,是我们想要的元素,则调用HUFFMAN_CODES.put()方法,将元素类型及其哈夫曼编码加进去

针对解释2的图解 

 6.创建一个压缩的方法🐻

public static byte[] zip(byte[] content,Map<Byte,String> huffmanCodes) {
        var strBuf = new StringBuilder();
        for (var item : content) {
            strBuf.append(huffmanCodes.get(item));
        }
        var len = (strBuf.length() + 7) / 8;
        var huffmanCodesBytes = new byte[len];
        String str;
        var index = 0;
        for (int i = 0; i < strBuf.length(); i+= 8) {
            if (i + 8 < strBuf.length()) {
                str = strBuf.substring(i,i+8);
            }else {
                str = strBuf.substring(i);
                lastCount = str.length();
            }
            huffmanCodesBytes[index++] = (byte) Integer.parseInt(str,2);
        }
        return huffmanCodesBytes;
    }

代码分析:🐨  

  1.  参数列表:
    1. 需要压缩的字符串对应的byte[]数组
    2. 哈夫曼编码表,用于压缩
  2. new一个StringBuilder,用于存放哈夫曼编码串
  3. 通过增强for循环,遍历里面的元素.根据哈夫曼编码表得出对应的哈夫曼编码并追加到哈夫曼编码串中
  4. 定义一个长度len(压缩后的数组的长度),并赋值为(strBuf.length() + 7) / 8
    1. 解释:压缩后的数组是byte[]数组,每个元素都是一个字节,每个字节有八个比特位
    2. 当最后一次不满八位的时候,我们也要存入数组中,所以我们要预留多一块空间
    3. +7相当于最后加了7位,保障了最后几位数组能够被存入数组中
  5. 创建对应的压缩后的数组以及临时字符串
  6. 创建对应的for循环,遍历哈夫曼编码串
    1. i = 0[从下标为0的元素开始];i < strBuf.length()[防止越界];i+=8[1byte = 8bit,每次取8位]
    2. 若i+8< strBuf.length()[,直接截断8位,即str = strBuf.substring(i,i+8);
    3. 否则说明已经到最后一个不足8位,若依然按照上述截断会发生异常,所以只需要 strBuf.substring(i),并将位数记录下来(解压时有巨大作用)
    4. 调用Integet.praseInt(str,2)方法,将二进制字符串转换为十进制数并存放如byte[]数组中,注意强制类型转换
  7. 将压缩后的byte[]数组返回即可

7.按步骤封装代码🐻 

public static byte[] huffmanCodeZip(String s) {
        var content = s.getBytes();
        var list = getNodes(content);
        var huffmanTreeRoot = createHuffmanTree(list);
        getCodes(huffmanTreeRoot,"",STRING_BUFFER);
        return zip(content,HUFFMAN_CODES);
    }

🐼结论

        压缩还是存在一定的难度,我们需要取理解其中的奥妙,解压过程中有更多的奥妙,敬请期待. 

  • 22
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 24
    评论
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爪哇土著、JOElib

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值