采用赫夫曼树完成数据压缩

huffmanZip方法为赫夫曼树压缩入口方法

public class HuffmanCode {
    /**
   * 使用一个方法,将前面的方法封装起来,便于我们的调用
   *
   * @param bytes 原始的数组
   * @return 压缩后的数组
   */
    public static byte[] huffmanZip(byte[] bytes) {
        // 1. 拿到List
        List<Node> nodes = getNodes(bytes);
        // 2. 创建赫夫曼树
        Node huffmanTreeRoot = createHuffmanTree(nodes);
        // 3. 获取对应的赫夫曼编码
        Map<Byte, String> huffmanCodes = getCodes(huffmanTreeRoot);
        // 4. 根据生成的赫夫曼编码,压缩得到压缩后的赫夫曼编码字节数组
        byte[] huffmanCodeBytes = zip(bytes, huffmanCodes);
        return huffmanCodeBytes;
    }

    /**
   * 编写一个方法,将字符串对应的byte[]数组,通过生成的赫夫曼编码表,返回一个赫夫曼编码,压缩后byte[]
   *
   * @param bytes        元素的字符串对应的byte[]
   * @param huffmanCodes 生成赫夫曼编码map
   * @return 返回赫夫曼编码处理后的byte[]
   * 举例:字符串“10101000”
   * 对应的byte[] huffmanCodeBytes,即对应一个byte,放入到huffmanCodeBytes
   * huffmanCodeBytes[0] = 10101000(补码) => byte [推导  10101000 => 10101000 - 1 => 10100111(反码) => 11011000 = -88]
   */
    private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
        // 先利用huffmanCodes 将bytes 转成赫夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        for (byte b : bytes) {
            stringBuilder.append(huffmanCodes.get(b));
        }
        // 将"10101000"转换成byte[]
        // 统计返回byte[] huffmanCodeBytes长度
        int len; //int len = (stringBuilder.length() + 7) / 8
        if (stringBuilder.length() % 8 == 0) {
            len = stringBuilder.length() / 8;
        } else {
            len = stringBuilder.length() / 8 + 1;
        }
        // 创建存储压缩后的byte数组
        byte[] huffmanCodeBytes = new byte[len];
        int index = 0;
        for (int i = 0; i < stringBuilder.length(); i += 8) {
            String strByte;
            if (i + 8 > stringBuilder.length())  //不够8位
                strByte = stringBuilder.substring(i);
            else
                strByte = stringBuilder.substring(i, i + 8);
            // 将strByte转成一个byte,放入到huffmanCodeBytes
            huffmanCodeBytes[index] = (byte) Integer.parseInt(strByte, 2);
            index++;
        }
        return huffmanCodeBytes;
    }

    // 生成赫夫曼编码表
    // 思路
    // 1. 将赫夫曼编码表存在Map<Byte,String> 形式:o: 1000   u: 10010  d: 100110  y: 100111  i: 101
    private static Map<Byte, String> huffmanCodes = new HashMap<>();
    // 2. 在生成赫夫曼编码表示,需要去拼接路径,定义一个StringBuilder,存储某个叶子节点的路径
    private static StringBuilder stringBuilder = new StringBuilder();

    public static Map<Byte, String> getCodes(Node root) {
        if (root == null) return null;
        getCodes(root.left, "0", stringBuilder);
        getCodes(root.right, "1", stringBuilder);
        return huffmanCodes;
    }

    /**
   * 将传入node节点的所有叶子节点的赫夫曼编码得到,并放入huffmanCodes中
   *
   * @param node          传入节点
   * @param code          路径:左子节点是0,右子节点是1
   * @param stringBuilder 用于拼接路径的
   */
    private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
        StringBuilder sb2 = new StringBuilder(stringBuilder);
        sb2.append(code);
        if (node != null) { //如果是空节点则不处理
            // 判断node是叶子节点还是非叶子节点
            if (node.data == null) {  //非叶子节点
                //向左递归
                getCodes(node.left, "0", sb2);
                // 向右递归
                getCodes(node.right, "1", sb2);
            } else { //叶子节点
                // 表示找到了某个叶子节点的最后
                huffmanCodes.put(node.data, sb2.toString());
            }
        }
    }

    public static void preOrder(Node root) {
        if (root != null) root.preOrder();
        else System.out.println("树为空");
    }

    /**
   * @param bytes 接受字节数组
   * @return 返回的是List的集合
   */
    private static List<Node> getNodes(byte[] bytes) {
        //1. 创建ArrayList
        ArrayList<Node> nodes = new ArrayList<>();
        // 存储每一个byte出现的次数-> map
        Map<Byte, Integer> counts = new HashMap<>();
        for (byte aByte : bytes) {
            Integer count = counts.get(aByte);
            if (count == null) {  //map还没有数据,第一次
                counts.put(aByte, 1);
            } else {
                counts.put(aByte, count + 1);
            }
        }

        // 每个键值对转成一个Node对象,放入nodes集合
        for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
            nodes.add(new Node(entry.getKey(), entry.getValue()));
        }
        return nodes;
    }

    // 通过List创建对应的赫夫曼树
    private static Node createHuffmanTree(List<Node> nodes) {
        while (nodes.size() > 1) {
            //对集合进行排序
            Collections.sort(nodes);
            //获取左最小的
            Node leftNode = nodes.get(0);
            //获取右子树最小的
            Node rightNode = nodes.get(1);
            //创建一颗新的二叉树
            Node parentNode = new Node(null, leftNode.weight + rightNode.weight);
            parentNode.left = leftNode;
            parentNode.right = rightNode;
            //移除二叉树的nodes
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parentNode);
        }
        // 返回根节点
        return nodes.get(0);
    }
}

//创建Node,带数值和权值
class Node implements Comparable<Node> {
    Byte data;  //存放数据本身
    int weight; //权值,表示字符出现的次数
    Node left;
    Node right;

    public Node(Byte data, int weight) {
        this.data = data;
        this.weight = weight;
    }


    @Override
    public int compareTo(Node o) {
        return this.weight - o.weight;
    }

    @Override
    public String toString() {
        return "Node{" +
            "data=" + data +
            ", weight=" + weight +
            '}';
    }

    // 前序遍历
    public void preOrder() {
        System.out.println(this);
        if (this.left != null) this.left.preOrder();
        if (this.right != null) this.right.preOrder();
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值