哈夫曼树(Huffman Tree,霍夫曼树,赫夫曼树)哈夫曼编码

哈夫曼树的相关概念:

重要概念

  1. 二叉树的带权路径长度:(叶子节点的路径长度 * 相应叶子节点的权值) 之和

  2. 哈夫曼树:给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近

在这里插入图片描述

在这里插入图片描述

哈夫曼树的特点:

节点的度:子节点的个数

哈夫曼树的特点:

  1. 权值越大的叶子节点越靠近根节点,而权值越小的叶子节点越远离根节点
  2. 只有子节点的个数为0的和子节点的个数为2的节点,不存在度为1的节点

在这里插入图片描述

哈夫曼算法基本思想(重要):

在这里插入图片描述

哈夫曼树的构建过程:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

哈夫曼树的存贮结构:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

代码实现构建哈夫曼树:

package com.fan.tree;
import java.util.ArrayList;
import java.util.Collections;

public class HuffmanTreeTest {
    public static void main(String[] args) {
        int[] arr ={13,7,8,3,29,6,1};
        HuffmanTree huffmanTree = new HuffmanTree();
        Node root = huffmanTree.createHuffmanTree(arr);
        System.out.println("哈夫曼树构建后,进行前序遍历进行验证:");
        huffmanTree.preOrder(root);
    }
}
class HuffmanTree{
    //前序遍历的方法
    public  void preOrder(Node root){
        if(root != null){
            root.preOrder();
        }else{
            System.out.println("是空树,不能遍历!!");
        }
    }
    //构建霍夫曼树的方法
    /**
     *
     * @param arr 一个权值数组
     * @return :哈夫曼树的根节点
     */
    public Node createHuffmanTree(int[] arr){
        //注意:这里的数组中放的是节点。
        /*
        一、初始化:构造只有根节点的 二叉树  集合
         */
        //1.遍历数组,将数组中的每个元素构成一个Node
        //2.将Node放入到ArrayList中
        ArrayList<Node> nodes = new ArrayList<>();
        for (int value : arr) {
            nodes.add(new Node(value));
        }
        /*
        当二叉树集合中只剩下一颗二叉树的时候,
        这颗二叉树就是哈夫曼树,所以我们这里用循环
         */
        while(nodes.size() > 1) {
        /*
        二、选取与合并:在只有根节点的 集合中,选取根节点的权值最小
        的两个节点 分别作为左右子树构造一个新的二叉树,且新构造的二叉树的
        根节点的权值为左右子树的权值之和
         */
            //将数组中的节点的权值从小到大排序
            Collections.sort(nodes);
            System.out.println("节点集合排序后:" + nodes);

            Node leftNode = nodes.get(0);//排序后的直接获取最小和次小的节点
            Node rightNode = nodes.get(1);
            //构建选取的那两个子树的 父/根节点
            Node parent = new Node(leftNode.weight + rightNode.weight);
            //设置这个新的二叉树的 左右子树
            parent.left = leftNode;
            parent.right = rightNode;
        /*
        三、删除与加入:在节点集合中,删除作为左右子树的两颗二叉树,
        并将新建立的二叉树加入到二叉树集合中
         */
            //从二叉树集合中 删除处理过的 二叉树
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            //将parent加入到 nodes二叉树集合中
            nodes.add(parent);
        /*
        四、重复二三两步,当二叉树集合中只剩下一颗二叉树的时候,
        这颗二叉树就是哈夫曼树,所以我们这里用循环
         */
        }//end-while
        //返回哈夫曼树的root节点
        return nodes.get(0);
    }
}
//节点是可排序的,实现可排序接口
class Node implements Comparable<Node>{
    int weight;//节点的权值
    Node left;//指向左子树的节点
    Node right;//指向右子树的节点

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

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

    @Override
    public int compareTo(Node o) {
        //表示从小到大排序
        return this.weight - o.weight;
    }
    //在节点类中写一个前序遍历
    public void preOrder(){
        //根左右,递归方法
        System.out.println(this);//输出当前节点
        if(this.left != null){
            this.left.preOrder();
        }
        if(this.right != null){
            this.right.preOrder();
        }
    }
}

霍夫曼编码,赫夫曼编码:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

哈夫曼编码是无损的处理方案

在这里插入图片描述

哈夫曼编码的思路:
在这里插入图片描述
代码:

package com.fan.tree.haffumantree;
import java.util.*;

public class HuffmanCode {
    public static void main(String[] args) {
    String str = "i like like like java do you like a java";
    //将字符串转成字节数组
        byte[] strBytes = str.getBytes();
        System.out.println(strBytes.length);//40字节长度
        List<Node> nodes = getNodes(strBytes);//构建list集合
        System.out.println("nodes="+nodes);
        //测试
        System.out.println("哈夫曼树");
        Node huffmanTreeRoot = createHuffmanTree(nodes);
        System.out.println("前序遍历:");
        preOrder(huffmanTreeRoot);//二叉树的遍历,调用静态方法
    }

    //前序遍历的方法
    private static void preOrder(Node root){
        if(root != null){
            root.preOrder();
        }else{
            System.out.println("树为空!!!");
        }
    }
    /**
     * 方法作用:将字节数组装成map形式存储,key是字母的字节本身,
     * value是出现的次数,然后将其每个封装成Node,并添加到list中
     *
     * @param bytes
     * @return list集合,其中存放的是Node
     */
    private static List<Node>  getNodes(byte[] bytes){
        //1.创建一个Arraylist
        ArrayList<Node> nodes = new ArrayList<>();
        //遍历bytes,统计每一个byte出现的次数,并放入到map中,map【k,v】
        HashMap<Byte, Integer> map = new HashMap<>();
        //for的作用是将byte数组中的字节以map形式存储,value是该字节出现的次数
        for (byte aByte : bytes) {
            Integer count = map.get(aByte);//开始是null,通过key,获取对应的value
            //给map中的value赋值,即出现的次数
            if(count ==null ){//map中还没有这个字符,第一次出现
                map.put(aByte,1);//存入到map中
            }else{
                map.put(aByte,count+1);//出现多次
            }
        }
        //把每一个键值对 转成一个NOde(date,weight属性) 对象,并加到我list集合nodes中
        //遍历map
        for (Map.Entry<Byte, Integer> entry : map.entrySet()) {
            //Node(T data, int weight)构造方法
            nodes.add(new Node(entry.getKey(),entry.getValue()));
        }
        return nodes;
    }

    /**
     * 构建一个哈夫曼树
     * @param nodes list集合,其中存的是Node节点的二叉树
     * @return 赶回二叉树的根节点
     */
    private static Node createHuffmanTree(List<Node> nodes){
    //1.初始化,即准备List集合,这里参数已经准备好了
        while(nodes.size() > 1){
            //排序后就好选取了
            Collections.sort(nodes);//从小到大排序的
            //2.选取与合并
            Node leftNode = nodes.get(0);//选取结合中的第一个元素,即最小权值的
            Node rightNode = nodes.get(1);
            Node<Byte> parent = new Node<>(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.right = rightNode;
            //3.删除与加入
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);//将新的二叉树加入到结合Node集合中
        }
        return nodes.get(0);//返回唯一的根节点,也就是哈夫曼树的根节点
    }
}

//节点类
class Node<T> implements Comparable<Node<T>>{
    T data;//存放的数据(如字符,a--97)
    int weight;//权值
    Node<T> left;//左子树节点
    Node<T> right;//右子树节点

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

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

    @Override
    public int compareTo(Node o) {
        //从小到大排序
        return this.weight - o.weight;
    }
    //前序遍历
    public void preOrder(){
        //根,左右
        //根:输出当前节点this,即正在调用的节点
        System.out.println(this);
        //左:当前节点的左子树不为空,继续向下遍历
        if(this.left != null){
            this.left.preOrder();
        }
        //左:当前节点的右子树不为空,继续向下遍历
        if(this.right != null){
            this.right.preOrder();
        }
    }
}

打印结果:

在这里插入图片描述
哈夫曼编码实现:

在这里插入图片描述
生成哈夫曼编码表代码:

package com.fan.tree.haffumantree;
import java.util.*;

public class HuffmanCode {
    public static void main(String[] args) {
    String str = "i like like like java do you like a java";
    //将字符串转成字节数组
        byte[] strBytes = str.getBytes();
        System.out.println(strBytes.length);//40字节长度
        List<Node> nodes = getNodes(strBytes);//构建list集合
        System.out.println("nodes="+nodes);
        //测试
        System.out.println("哈夫曼树");
        Node huffmanTreeRoot = createHuffmanTree(nodes);
        System.out.println("前序遍历:");
        preOrder(huffmanTreeRoot);//二叉树的遍历,调用静态方法
        //测试是否生成了对应的哈夫曼编码表
        haffumanCodesMap = getCodesMap(huffmanTreeRoot, "", stringBuilder);
        System.out.println("生成的哈夫曼编码表:"+haffumanCodesMap);
    }

    //前序遍历的方法
    private static void preOrder(Node root){
        if(root != null){
            root.preOrder();
        }else{
            System.out.println("树为空!!!");
        }
    }
    /**
     * 方法作用:将字节数组装成map形式存储,key是字母的字节本身,
     * value是出现的次数,然后将其每个封装成Node,并添加到list中
     *
     * @param bytes
     * @return list集合,其中存放的是Node
     */
    private static List<Node>  getNodes(byte[] bytes){
        //1.创建一个Arraylist
        ArrayList<Node> nodes = new ArrayList<>();
        //遍历bytes,统计每一个byte出现的次数,并放入到map中,map【k,v】
        HashMap<Byte, Integer> map = new HashMap<>();
        //for的作用是将byte数组中的字节以map形式存储,value是该字节出现的次数
        for (byte aByte : bytes) {
            Integer count = map.get(aByte);//开始是null,通过key,获取对应的value
            //给map中的value赋值,即出现的次数
            if(count ==null ){//map中还没有这个字符,第一次出现
                map.put(aByte,1);//存入到map中
            }else{
                map.put(aByte,count+1);//出现多次
            }
        }
        //把每一个键值对 转成一个NOde(date,weight属性) 对象,并加到我list集合nodes中
        //遍历map
        for (Map.Entry<Byte, Integer> entry : map.entrySet()) {
            //Node(T data, int weight)构造方法
            nodes.add(new Node(entry.getKey(),entry.getValue()));
        }
        return nodes;
    }

    /**
     * 构建一个哈夫曼树
     * @param nodes list集合,其中存的是Node节点的二叉树
     * @return 赶回二叉树的根节点
     */
    private static Node createHuffmanTree(List<Node> nodes){
    //1.初始化,即准备List集合,这里参数已经准备好了
        while(nodes.size() > 1){
            //排序后就好选取了
            Collections.sort(nodes);//从小到大排序的
            //2.选取与合并
            Node leftNode = nodes.get(0);//选取结合中的第一个元素,即最小权值的
            Node rightNode = nodes.get(1);
            Node<Byte> parent = new Node<>(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.right = rightNode;
            //3.删除与加入
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);//将新的二叉树加入到结合Node集合中
        }
        return nodes.get(0);//返回唯一的根节点,也就是哈夫曼树的根节点
    }
    //利用哈夫曼树 生成对应的哈夫曼编码表
    /*
    思路:
    1.将哈夫曼编码表存放到Map中,形式32->01  97->100 100->11000等等
        key是字符的byte,value是字符对应的的哈夫曼编码
     */
    static Map<Byte,String> haffumanCodesMap= new HashMap<Byte,String>();
    //2.在生成的哈夫曼编码的时候,需要拼接路径,
    //定义一个StringBuilder来存储叶子节点的路径
    static StringBuilder stringBuilder = new StringBuilder();

    /**
     * 
     * @param node 传入节点
     * @param code 路径:左子节点是0,右子节点是1
     * @param stringBuilder  用于拼接路径
     */
    private static Map<Byte,String> getCodesMap(Node<Byte> node,String code,StringBuilder stringBuilder){
        StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
        //将code加入到stringBuilder2
        stringBuilder2.append(code);//追加
        if(node != null){//如果node==null,不处理
            //判断当前node 是叶子节点还是非叶子节点
            if(node.data == null){//非叶子节点:数据域为空,权值不为空
                //递归处理,向左递归
                getCodesMap(node.left,"0",stringBuilder2);
                //递归处理,向右递归
                getCodesMap(node.right,"1",stringBuilder2);
            }else{//处理叶子节点
                //key是字符的byte,value是字符对应的的哈夫曼编码,String类型的
                haffumanCodesMap.put(node.data,stringBuilder2.toString());
            }
        }
        return haffumanCodesMap;
    }
}

//节点类
class Node<T> implements Comparable<Node<T>>{
    T data;//存放的数据(如字符,a--97)
    int weight;//权值
    Node<T> left;//左子树节点
    Node<T> right;//右子树节点

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

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

    @Override
    public int compareTo(Node o) {
        //从小到大排序
        return this.weight - o.weight;
    }
    //前序遍历
    public void preOrder(){
        //根,左右
        //根:输出当前节点this,即正在调用的节点
        System.out.println(this);
        //左:当前节点的左子树不为空,继续向下遍历
        if(this.left != null){
            this.left.preOrder();
        }
        //左:当前节点的右子树不为空,继续向下遍历
        if(this.right != null){
            this.right.preOrder();
        }
    }
}

在这里插入图片描述

在这里插入图片描述

package com.fan.tree.haffumantree;
import java.util.*;

public class HuffmanCode {
    public static void main(String[] args) {
    String str = "i like like like java do you like a java";
    //将字符串转成字节数组
        byte[] strBytes = str.getBytes();
        System.out.println(strBytes.length);//40字节长度
        /*
        List<Node> nodes = getNodes(strBytes);//构建list集合
        System.out.println("nodes="+nodes);
        //测试
        System.out.println("哈夫曼树");
        Node huffmanTreeRoot = createHuffmanTree(nodes);
        System.out.println("前序遍历:");
        preOrder(huffmanTreeRoot);//二叉树的遍历,调用静态方法
        //测试是否生成了对应的哈夫曼编码表
        haffumanCodesMap = getHaffumanCodesMap(huffmanTreeRoot, "", stringBuilder);
        System.out.println("生成的哈夫曼编码表:"+haffumanCodesMap);
        //测试
        byte[] huffmanCodeBytes = zip(strBytes, haffumanCodesMap);
        System.out.println("huffmanCodeBytes="+ Arrays.toString(huffmanCodeBytes));//17字节
        */
        byte[] haffumanCodesBytes = huffmanZip(strBytes);
        System.out.println("压缩后的结果是--:"+Arrays.toString(haffumanCodesBytes)+
                ",长度="+haffumanCodesBytes.length);
    }

    //使用一个方法,将前面的方法封装起来,方便我们调用
    private static byte[] huffmanZip(byte[] bytes){
        List<Node> nodes = getNodes(bytes);
        //根据nodes创建 哈夫曼树
        Node huffmanTreeRoot = createHuffmanTree(nodes);
        //根据哈夫曼树 创建  对应的 哈夫曼编码表 map表
        Map<Byte,String> haffumanCodesMap =
                getHaffumanCodesMap(huffmanTreeRoot);
        //根据生成的 哈夫曼编码表,亚索得到 压缩后的 哈夫曼编码字节数组
        byte[] haffumanCodesBytes = zip(bytes, haffumanCodesMap);
        return haffumanCodesBytes;
    }

    //前序遍历的方法
    private static void preOrder(Node root){
        if(root != null){
            root.preOrder();
        }else{
            System.out.println("树为空!!!");
        }
    }
    /**
     * 二、方法作用:将字节数组装成map形式存储,key是字母的字节本身,
     * value是出现的次数,然后将其每个封装成Node,并添加到list中
     *
     * @param bytes
     * @return list集合,其中存放的是Node
     */
    private static List<Node>  getNodes(byte[] bytes){
        //1.创建一个Arraylist
        ArrayList<Node> nodes = new ArrayList<>();
        //遍历bytes,统计每一个byte出现的次数,并放入到map中,map【k,v】
        HashMap<Byte, Integer> map = new HashMap<>();
        //for的作用是将byte数组中的字节以map形式存储,value是该字节出现的次数
        for (byte aByte : bytes) {
            Integer count = map.get(aByte);//开始是null,通过key,获取对应的value
            //给map中的value赋值,即出现的次数
            if(count ==null ){//map中还没有这个字符,第一次出现
                map.put(aByte,1);//存入到map中
            }else{
                map.put(aByte,count+1);//出现多次
            }
        }
        //把每一个键值对 转成一个NOde(date,weight属性) 对象,并加到我list集合nodes中
        //遍历map
        for (Map.Entry<Byte, Integer> entry : map.entrySet()) {
            //Node(T data, int weight)构造方法
            nodes.add(new Node(entry.getKey(),entry.getValue()));
        }
        return nodes;
    }

    /**
     * 三、构建一个哈夫曼树
     * @param nodes list集合,其中存的是Node节点的二叉树
     * @return 赶回二叉树的根节点
     */
    private static Node createHuffmanTree(List<Node> nodes){
    //1.初始化,即准备List集合,这里参数已经准备好了
        while(nodes.size() > 1){
            //排序后就好选取了
            Collections.sort(nodes);//从小到大排序的
            //2.选取与合并
            Node leftNode = nodes.get(0);//选取结合中的第一个元素,即最小权值的
            Node rightNode = nodes.get(1);
            Node<Byte> parent = new Node<>(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.right = rightNode;
            //3.删除与加入
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);//将新的二叉树加入到结合Node集合中
        }
        return nodes.get(0);//返回唯一的根节点,也就是哈夫曼树的根节点
    }
    //利用哈夫曼树 生成对应的哈夫曼编码表
    /*
    思路:
    1.将哈夫曼编码表存放到Map中,形式32->01  97->100 100->11000等等
        key是字符的byte,value是字符对应的的哈夫曼编码
     */
    static Map<Byte,String> haffumanCodesMap= new HashMap<Byte,String>();
    //2.在生成的哈夫曼编码的时候,需要拼接路径,
    //定义一个StringBuilder来存储叶子节点的路径
    static StringBuilder stringBuilder = new StringBuilder();
    //getHaffumanCodesMap重载的方法--为了方便调用
    private static Map<Byte,String> getHaffumanCodesMap(Node<Byte> node){
        Map<Byte, String> haffumanCodesMap =
                getHaffumanCodesMap(node, "", stringBuilder);
        return haffumanCodesMap;
    }

        /**
         * 四、方法作用:利用哈夫曼树 生成对应的哈夫曼编码表,map表
         * @param node 传入节点
         * @param code 路径:左子节点是0,右子节点是1
         * @param stringBuilder  用于拼接路径
         */
    private static Map<Byte,String> getHaffumanCodesMap
    (Node<Byte> node,String code,StringBuilder stringBuilder){
        StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
        //将code加入到stringBuilder2
        stringBuilder2.append(code);//追加
        if(node != null){//如果node==null,不处理
            //判断当前node 是叶子节点还是非叶子节点
            if(node.data == null){//非叶子节点:数据域为空,权值不为空
                //递归处理,向左递归
                getHaffumanCodesMap(node.left,"0",stringBuilder2);
                //递归处理,向右递归
                getHaffumanCodesMap(node.right,"1",stringBuilder2);
            }else{//处理叶子节点
                //key是字符的byte,value是字符对应的的哈夫曼编码,String类型的
                haffumanCodesMap.put(node.data,stringBuilder2.toString());
            }
        }
        return haffumanCodesMap;
    }

    /**
     *  五、将原始字节数组  利用 哈夫曼编码表 进行 编码,
     * 返回压缩后的 字节数组
     * @param bytes 原始的字符串对应的byte[]
     * @param haffumanCodesMap 生成的哈夫曼编码表map
     * @return 返回一个哈夫曼编码 压缩后的 字节数组
     */
    private static byte[] zip(byte[] bytes,Map<Byte,String> haffumanCodesMap){
        //1.利用haffumanCodesMap  将 bytes 转成 哈夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        //遍历bytes 数组,将所有字符对应的哈夫曼编码穿到一个字符串中
        for (byte aByte : bytes) {
            //haffumanCodesMap.get(aByte):字符对应的哈夫曼编码
            stringBuilder.append(haffumanCodesMap.get(aByte));
        }
        System.out.println("测试stringbuilder:"+stringBuilder.toString());
        /*
        将"11010001111..."转成对应的byte数组,便于网络传输
        统计返回byte[] haffumanCodesMapBytes长度
         */
        int len;
        //一句话:int len =( stringBuilder.length() +7 ) /8
        if(stringBuilder.length() % 8 == 0){
            len = stringBuilder.length() / 8;//8位字符组成 一个字节
        }else{
            len = stringBuilder.length() / 8 + 1;
        }
        //创建存储压缩后 的byte数组
        byte[] haffumanCodesBytes = new byte[len];
        int index = 0;//记录是第几个byte
        for (int i = 0; i < stringBuilder.length(); i+=8) {
            String strByte;
            if(i+8 > stringBuilder.length()){
                //stringBuilder长度不够8位
                strByte = stringBuilder.substring(i);//从i开始到末尾截取
            }else{
                strByte = stringBuilder.substring(i,i+8);//截取8位
            }
            //将strByte 转成一个byte,放入到haffumanCodesMapBytes
            //Integer.parseInt(strByte,2):将一个字符串如1001 1001 按照二进制转成int
            //string-->int -->byte然后放入byte数组中
            haffumanCodesBytes[index] = (byte)Integer.parseInt(strByte,2);
            index++;
        }
        return haffumanCodesBytes;
    }
}

//一、节点类
class Node<T> implements Comparable<Node<T>>{
    T data;//存放的数据(如字符,a--97)
    int weight;//权值
    Node<T> left;//左子树节点
    Node<T> right;//右子树节点

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

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

    @Override
    public int compareTo(Node o) {
        //从小到大排序
        return this.weight - o.weight;
    }
    //前序遍历
    public void preOrder(){
        //根,左右
        //根:输出当前节点this,即正在调用的节点
        System.out.println(this);
        //左:当前节点的左子树不为空,继续向下遍历
        if(this.left != null){
            this.left.preOrder();
        }
        //左:当前节点的右子树不为空,继续向下遍历
        if(this.right != null){
            this.right.preOrder();
        }
    }
}

数据解码:

在这里插入图片描述

代码实现解码:

package com.fan.tree.haffumantree;
import java.util.*;

public class HuffmanCode {
    public static void main(String[] args) {
    String str = "i like like like java do you like a java";
    //将字符串转成字节数组
        byte[] strBytes = str.getBytes();
        System.out.println(strBytes.length);//40字节长度
        /*
        List<Node> nodes = getNodes(strBytes);//构建list集合
        System.out.println("nodes="+nodes);
        //测试
        System.out.println("哈夫曼树");
        Node huffmanTreeRoot = createHuffmanTree(nodes);
        System.out.println("前序遍历:");
        preOrder(huffmanTreeRoot);//二叉树的遍历,调用静态方法
        //测试是否生成了对应的哈夫曼编码表
        haffumanCodesMap = getHaffumanCodesMap(huffmanTreeRoot, "", stringBuilder);
        System.out.println("生成的哈夫曼编码表:"+haffumanCodesMap);
        //测试
        byte[] huffmanCodeBytes = zip(strBytes, haffumanCodesMap);
        System.out.println("huffmanCodeBytes="+ Arrays.toString(huffmanCodeBytes));//17字节个大小
        */
        byte[] haffumanCodesBytes = huffmanZip(strBytes);
        System.out.println("压缩后的结果是--:"+Arrays.toString(haffumanCodesBytes)+
                ",长度="+haffumanCodesBytes.length);

        //测试解码
        byte[] decode = decode(haffumanCodesMap, haffumanCodesBytes);
        System.out.println("解码后:"+new String(decode));//使用new String(byte[] b)
    }

    //使用一个方法,将前面的方法封装起来,方便我们调用
    private static byte[] huffmanZip(byte[] bytes){
        List<Node> nodes = getNodes(bytes);
        //根据nodes创建 哈夫曼树
        Node huffmanTreeRoot = createHuffmanTree(nodes);
        //根据哈夫曼树 创建  对应的 哈夫曼编码表 map表
        Map<Byte,String> haffumanCodesMap =
                getHaffumanCodesMap(huffmanTreeRoot);
        //根据生成的 哈夫曼编码表,亚索得到 压缩后的 哈夫曼编码字节数组
        byte[] haffumanCodesBytes = zip(bytes, haffumanCodesMap);
        return haffumanCodesBytes;
    }

    //前序遍历的方法
    private static void preOrder(Node root){
        if(root != null){
            root.preOrder();
        }else{
            System.out.println("树为空!!!");
        }
    }
    /**
     * 二、方法作用:将字节数组装成map形式存储,key是字母的字节本身,
     * value是出现的次数,然后将其每个封装成Node,并添加到list中
     *
     * @param bytes
     * @return list集合,其中存放的是Node
     */
    private static List<Node>  getNodes(byte[] bytes){
        //1.创建一个Arraylist
        ArrayList<Node> nodes = new ArrayList<>();
        //遍历bytes,统计每一个byte出现的次数,并放入到map中,map【k,v】
        HashMap<Byte, Integer> map = new HashMap<>();
        //for的作用是将byte数组中的字节以map形式存储,value是该字节出现的次数
        for (byte aByte : bytes) {
            Integer count = map.get(aByte);//开始是null,通过key,获取对应的value
            //给map中的value赋值,即出现的次数
            if(count ==null ){//map中还没有这个字符,第一次出现
                map.put(aByte,1);//存入到map中
            }else{
                map.put(aByte,count+1);//出现多次
            }
        }
        //把每一个键值对 转成一个NOde(date,weight属性) 对象,并加到我list集合nodes中
        //遍历map
        for (Map.Entry<Byte, Integer> entry : map.entrySet()) {
            //Node(T data, int weight)构造方法
            nodes.add(new Node(entry.getKey(),entry.getValue()));
        }
        return nodes;
    }

    /**
     * 三、构建一个哈夫曼树
     * @param nodes list集合,其中存的是Node节点的二叉树
     * @return 赶回二叉树的根节点
     */
    private static Node createHuffmanTree(List<Node> nodes){
    //1.初始化,即准备List集合,这里参数已经准备好了
        while(nodes.size() > 1){
            //排序后就好选取了
            Collections.sort(nodes);//从小到大排序的
            //2.选取与合并
            Node leftNode = nodes.get(0);//选取结合中的第一个元素,即最小权值的
            Node rightNode = nodes.get(1);
            Node<Byte> parent = new Node<>(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.right = rightNode;
            //3.删除与加入
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);//将新的二叉树加入到结合Node集合中
        }
        return nodes.get(0);//返回唯一的根节点,也就是哈夫曼树的根节点
    }
    //利用哈夫曼树 生成对应的哈夫曼编码表
    /*
    思路:
    1.将哈夫曼编码表存放到Map中,形式32->01  97->100 100->11000等等
        key是字符的byte,value是字符对应的的哈夫曼编码
     */
    static Map<Byte,String> haffumanCodesMap= new HashMap<Byte,String>();
    //2.在生成的哈夫曼编码的时候,需要拼接路径,
    //定义一个StringBuilder来存储叶子节点的路径
    static StringBuilder stringBuilder = new StringBuilder();
    //getHaffumanCodesMap重载的方法--为了方便调用
    private static Map<Byte,String> getHaffumanCodesMap(Node<Byte> node){
        Map<Byte, String> haffumanCodesMap =
                getHaffumanCodesMap(node, "", stringBuilder);
        return haffumanCodesMap;
    }

        /**
         * 四、方法作用:利用哈夫曼树 生成对应的哈夫曼编码表,map表
         * @param node 传入节点
         * @param code 路径:左子节点是0,右子节点是1
         * @param stringBuilder  用于拼接路径
         */
    private static Map<Byte,String> getHaffumanCodesMap(Node<Byte> node,String code,StringBuilder stringBuilder){
        StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
        //将code加入到stringBuilder2
        stringBuilder2.append(code);//追加
        if(node != null){//如果node==null,不处理
            //判断当前node 是叶子节点还是非叶子节点
            if(node.data == null){//非叶子节点:数据域为空,权值不为空
                //递归处理,向左递归
                getHaffumanCodesMap(node.left,"0",stringBuilder2);
                //递归处理,向右递归
                getHaffumanCodesMap(node.right,"1",stringBuilder2);
            }else{//处理叶子节点
                //key是字符的byte,value是字符对应的的哈夫曼编码,String类型的
                haffumanCodesMap.put(node.data,stringBuilder2.toString());
            }
        }
        return haffumanCodesMap;
    }

    /**
     *  五、将原始字节数组  利用 哈夫曼编码表 进行 编码,
     * 返回压缩后的 字节数组
     * @param bytes 原始的字符串对应的byte[]
     * @param haffumanCodesMap 生成的哈夫曼编码表map
     * @return 返回一个哈夫曼编码 压缩后的 字节数组
     */
    private static byte[] zip(byte[] bytes,Map<Byte,String> haffumanCodesMap){
        //1.利用haffumanCodesMap  将 bytes 转成 哈夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        //遍历bytes 数组,将所有字符对应的哈夫曼编码穿到一个字符串中
        for (byte aByte : bytes) {
            //haffumanCodesMap.get(aByte):字符对应的哈夫曼编码
            stringBuilder.append(haffumanCodesMap.get(aByte));
        }
        System.out.println("测试stringbuilder:"+stringBuilder.toString());
        /*
        将"11010001111..."转成对应的byte数组,便于网络传输
        统计返回byte[] haffumanCodesMapBytes长度
         */
        int len;
        //一句话:int len =( stringBuilder.length() +7 ) /8
        if(stringBuilder.length() % 8 == 0){
            len = stringBuilder.length() / 8;//8位字符组成 一个字节
        }else{
            len = stringBuilder.length() / 8 + 1;
        }
        //创建存储压缩后 的byte数组
        byte[] haffumanCodesBytes = new byte[len];
        int index = 0;//记录是第几个byte
        for (int i = 0; i < stringBuilder.length(); i+=8) {
            String strByte;
            if(i+8 > stringBuilder.length()){
                //stringBuilder长度不够8位
                strByte = stringBuilder.substring(i);//从i开始到末尾截取
            }else{
                strByte = stringBuilder.substring(i,i+8);//截取8位
            }
            //将strByte 转成一个byte,放入到haffumanCodesMapBytes
            //Integer.parseInt(strByte,2):将一个字符串如1001 1001 按照二进制转成int
            //string-->int -->byte然后放入byte数组中
            haffumanCodesBytes[index] = (byte)Integer.parseInt(strByte,2);
            index++;
        }
        return haffumanCodesBytes;
    }

    //-------------------------数据的解码-----------------------------------------

    /**
     *
     * @param b 传入的一个字节byte
     * @param flag:标识是否需要补高位,如果是true,表示需要补高位,false表示不补
     * @return :传入的字节 对应的二进制 字符串(注意是按补码返回)
     */
    private static String byteToBitString(boolean flag,byte b){
        //使用变量保存b
        int temp = b;//将b转成int,因为Integer.toBinaryString(int i)参数是int
        //如果是正数,我们还需要补高位
        if(flag){
            temp |= 256;//按位与 256,如1 0000 0000 | 0000 0001 -->1 0000 0001
        }
        //返回的是temp对应的二进制的补码
        String str = Integer.toBinaryString(temp);
        if(flag){
            return str.substring(str.length() -8);
        }else{
            return str;
        }
    }
    //
    private static byte[] decode(Map<Byte,String> huffmanCodesMap,
                                 byte[] haffumanCodesBytes){
        //1.先得到haffumanCodesBytes 对应的二进制字符串,形式10101000...
        StringBuilder stringBuilder = new StringBuilder();
        //将byte数组转成 二进制 的字符串
        for (int i = 0; i < haffumanCodesBytes.length; i++) {
            byte b = haffumanCodesBytes[i];//取出每一个字节
            //判断是不是最后一个字节
            boolean flag = (i == haffumanCodesBytes.length - 1);
            //不是最后一个字节,追加
            stringBuilder.append(byteToBitString(!flag,b));
        }
        System.out.println("hafuman字节数组对应的二进制字符串:"+
                stringBuilder.toString());
        //把字符串 按照指定的哈夫曼编码表 进行解码
        //把哈夫曼编码表 进行调换,因为要反向查询 a->100 ,100->a
        HashMap<String, Byte> map = new HashMap<>();//反向的存储结构
        for (Map.Entry<Byte, String> entry : huffmanCodesMap.entrySet()) {
            map.put(entry.getValue(),entry.getKey());
        }
        System.out.println("map="+map);
        //创建 给定集合,存放byte
        ArrayList<Byte> list = new ArrayList<>();
        //i 可以理解为索引,扫描stringBuilder
        for (int i = 0; i < stringBuilder.length(); ) {
            int count = 1;//小的计数器
            boolean flag = true;
            Byte b = null;
            while(flag){
                //i 不动哦那个,让count移动,指定匹配到一个字符
                //递增的取出 key 1
                String key = stringBuilder.substring(i, i + count);
                b = map.get(key);
                if(b == null){//说明没有匹配到
                    count++;
                }else{//匹配到
                    flag = false;
                }
            }
            list.add(b);
            i+= count;//i直接移动到count
        }
        //当for循环结束后,我们list中就存放了所有的字符"1 like like like..."
        byte[] b = new byte[list.size()];
        for (int i = 0; i < b.length; i++) {
            //把list中的数据 放入到byte[] 并返回
            b[i] = list.get(i);
        }
        return b;
    }
}

//一、节点类
class Node<T> implements Comparable<Node<T>>{
    T data;//存放的数据(如字符,a--97)
    int weight;//权值
    Node<T> left;//左子树节点
    Node<T> right;//右子树节点

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

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

    @Override
    public int compareTo(Node o) {
        //从小到大排序
        return this.weight - o.weight;
    }
    //前序遍历
    public void preOrder(){
        //根,左右
        //根:输出当前节点this,即正在调用的节点
        System.out.println(this);
        //左:当前节点的左子树不为空,继续向下遍历
        if(this.left != null){
            this.left.preOrder();
        }
        //左:当前节点的右子树不为空,继续向下遍历
        if(this.right != null){
            this.right.preOrder();
        }
    }
}

在这里插入图片描述

最终测试结果:
在这里插入图片描述

文件压缩:如图片压缩:

在这里插入图片描述

//文件的压缩方法
    public static void zipFile(String srcFile,String desFile){
        //创建输出流
        FileOutputStream os = null;
        //创建对象输出流
        ObjectOutputStream oos = null;
        //创建文件的输入流
        FileInputStream is = null;
        try{
            //创建文件的输入流
            is = new FileInputStream(srcFile);
            //创建一个和源文件大小一样的byte[],将源文件转成字节数组
            byte[] b = new byte[is.available()];
            //读取文件,读取源文件的字节数组
            is.read(b);
            //直接对源文件压缩
            byte[] huffmanBytes = huffmanZip(b);//压缩字节数组
            //创建文件的输出流,存放压缩文件
            os = new FileOutputStream(desFile);
            oos = new ObjectOutputStream(os);
            //把哈夫曼编码后的字节数组 写入到压缩文件
            oos.writeObject(huffmanBytes);
            //再写入一个编码表对象
            oos.writeObject(haffumanCodesMap);
        }catch (Exception e){
            try {
                oos.close();
                os.close();
                is.close();
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
    }

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

最终代码:(无法解压文件:)

package com.fan.tree.haffumantree;
import java.io.*;
import java.util.*;

public class HuffmanCode {
    public static void main(String[] args) {
    String str = "i like like like java do you like a java";
    //将字符串转成字节数组
        byte[] strBytes = str.getBytes();
        System.out.println(strBytes.length);//40字节长度
        /*
        List<Node> nodes = getNodes(strBytes);//构建list集合
        System.out.println("nodes="+nodes);
        //测试
        System.out.println("哈夫曼树");
        Node huffmanTreeRoot = createHuffmanTree(nodes);
        System.out.println("前序遍历:");
        preOrder(huffmanTreeRoot);//二叉树的遍历,调用静态方法
        //测试是否生成了对应的哈夫曼编码表
        haffumanCodesMap = getHaffumanCodesMap(huffmanTreeRoot, "", stringBuilder);
        System.out.println("生成的哈夫曼编码表:"+haffumanCodesMap);
        //测试
        byte[] huffmanCodeBytes = zip(strBytes, haffumanCodesMap);
        System.out.println("huffmanCodeBytes="+ Arrays.toString(huffmanCodeBytes));//17字节个大小
        */
        byte[] haffumanCodesBytes = huffmanZip(strBytes);
        System.out.println("压缩后的结果是--:"+Arrays.toString(haffumanCodesBytes)+
                ",长度="+haffumanCodesBytes.length);

        //测试解码
        byte[] decode = decode(haffumanCodesBytes,haffumanCodesMap );
        System.out.println("解码后:"+new String(decode));//使用new String(byte[] b)
        //测试压缩文件
        /*String srcFile = "d://test.bmp";
        String desFile = "d://des2.zip";
        zipFile(srcFile,desFile);
        System.out.println("压缩文件完毕!!!");*/
        //测试解码
        String zipFile = "d://des2.zip";
        String des2File = "d://src2.jpg";
        unZip(zipFile,des2File);
        System.out.println("解压成功");
    }

    //使用一个方法,将前面的方法封装起来,方便我们调用
    private static byte[] huffmanZip(byte[] bytes){
        //获取字母的权值,并封装到list中
        List<Node> nodes = getNodes(bytes);
        //根据nodes创建 哈夫曼树
        Node huffmanTreeRoot = createHuffmanTree(nodes);
        //根据哈夫曼树 创建  对应的 哈夫曼编码表 map表
        Map<Byte,String> haffumanCodesMap =
                getHaffumanCodesMap(huffmanTreeRoot);
        //根据生成的 哈夫曼编码表,亚索得到 压缩后的 哈夫曼编码字节数组
        byte[] haffumanCodesBytes = zip(bytes, haffumanCodesMap);
        return haffumanCodesBytes;
    }

    //前序遍历的方法
    private static void preOrder(Node root){
        if(root != null){
            root.preOrder();
        }else{
            System.out.println("树为空!!!");
        }
    }
    /**
     * 二、方法作用:将字节数组装成map形式存储,key是字母的字节本身,
     * value是出现的次数,然后将其每个封装成Node,并添加到list中
     *
     * @param bytes
     * @return list集合,其中存放的是Node
     */
    private static List<Node>  getNodes(byte[] bytes){
        //1.创建一个Arraylist
        ArrayList<Node> nodes = new ArrayList<>();
        //遍历bytes,统计每一个byte出现的次数,并放入到map中,map【k,v】
        HashMap<Byte, Integer> map = new HashMap<>();
        //for的作用是将byte数组中的字节以map形式存储,value是该字节出现的次数
        for (byte aByte : bytes) {
            Integer count = map.get(aByte);//开始是null,通过key,获取对应的value
            //给map中的value赋值,即出现的次数
            if(count ==null ){//map中还没有这个字符,第一次出现
                map.put(aByte,1);//存入到map中
            }else{
                map.put(aByte,count+1);//出现多次
            }
        }
        //把每一个键值对 转成一个NOde(date,weight属性) 对象,并加到我list集合nodes中
        //遍历map
        for (Map.Entry<Byte, Integer> entry : map.entrySet()) {
            //Node(T data, int weight)构造方法
            nodes.add(new Node(entry.getKey(),entry.getValue()));
        }
        return nodes;
    }

    /**
     * 三、构建一个哈夫曼树
     * @param nodes list集合,其中存的是Node节点的二叉树
     * @return 赶回二叉树的根节点
     */
    private static Node createHuffmanTree(List<Node> nodes){
    //1.初始化,即准备List集合,这里参数已经准备好了
        while(nodes.size() > 1){
            //排序后就好选取了
            Collections.sort(nodes);//从小到大排序的
            //2.选取与合并
            Node leftNode = nodes.get(0);//选取结合中的第一个元素,即最小权值的
            Node rightNode = nodes.get(1);
            Node<Byte> parent = new Node<>(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.right = rightNode;
            //3.删除与加入
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);//将新的二叉树加入到结合Node集合中
        }
        return nodes.get(0);//返回唯一的根节点,也就是哈夫曼树的根节点
    }
    //利用哈夫曼树 生成对应的哈夫曼编码表
    /*
    思路:
    1.将哈夫曼编码表存放到Map中,形式32->01  97->100 100->11000等等
        key是字符的byte,value是字符对应的的哈夫曼编码
     */
    static Map<Byte,String> haffumanCodesMap= new HashMap<Byte,String>();
    //2.在生成的哈夫曼编码的时候,需要拼接路径,
    //定义一个StringBuilder来存储叶子节点的路径
    static StringBuilder stringBuilder = new StringBuilder();
    //getHaffumanCodesMap重载的方法--为了方便调用
    private static Map<Byte,String> getHaffumanCodesMap(Node<Byte> root){
        if(root == null){
            return null;
        }
        //处理root左子树
        getHaffumanCodesMap(root.left,"0",stringBuilder);
        //处理root右子树
        getHaffumanCodesMap(root.right,"1",stringBuilder);
        return haffumanCodesMap;
    }

        /**
         * 四、方法作用:利用哈夫曼树 生成对应的哈夫曼编码表,map表
         * @param node 传入节点
         * @param code 路径:左子节点是0,右子节点是1
         * @param stringBuilder  用于拼接路径
         */
    private static void getHaffumanCodesMap(Node<Byte> node,String code,StringBuilder stringBuilder){
        StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
        //将code加入到stringBuilder2
        stringBuilder2.append(code);//追加
        if(node != null){//如果node==null,不处理
            //判断当前node 是叶子节点还是非叶子节点
            if(node.data == null){//非叶子节点:数据域为空,权值不为空
                //递归处理,向左递归
                getHaffumanCodesMap(node.left,"0",stringBuilder2);
                //递归处理,向右递归
                getHaffumanCodesMap(node.right,"1",stringBuilder2);
            }else{//处理叶子节点
                //key是字符的byte,value是字符对应的的哈夫曼编码,String类型的
                haffumanCodesMap.put(node.data,stringBuilder2.toString());
            }
        }

    }

    /**
     *  五、将原始字节数组  利用 哈夫曼编码表 进行 编码,
     * 返回压缩后的 字节数组
     * @param bytes 原始的字符串对应的byte[]
     * @param haffumanCodesMap 生成的哈夫曼编码表map
     * @return 返回一个哈夫曼编码 压缩后的 字节数组
     */
    private static byte[] zip(byte[] bytes,Map<Byte,String> haffumanCodesMap){
        //1.利用haffumanCodesMap  将 bytes 转成 哈夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        //遍历bytes 数组,将所有字符对应的哈夫曼编码穿到一个字符串中
        for (byte aByte : bytes) {
            //haffumanCodesMap.get(aByte):字符对应的哈夫曼编码
            stringBuilder.append(haffumanCodesMap.get(aByte));
        }
        System.out.println("测试stringbuilder:"+stringBuilder.toString());
        /*
        将"11010001111..."转成对应的byte数组,便于网络传输
        统计返回byte[] haffumanCodesMapBytes长度
         */
        int len;
        //一句话:int len =( stringBuilder.length() +7 ) /8
        if(stringBuilder.length() % 8 == 0){
            len = stringBuilder.length() / 8;//8位字符组成 一个字节
        }else{
            len = stringBuilder.length() / 8 + 1;
        }
        //创建存储压缩后 的byte数组
        byte[] haffumanCodesBytes = new byte[len];
        int index = 0;//记录是第几个byte
        for (int i = 0; i < stringBuilder.length(); i+=8) {
            String strByte;
            if(i+8 > stringBuilder.length()){
                //stringBuilder长度不够8位
                strByte = stringBuilder.substring(i);//从i开始到末尾截取
            }else{
                strByte = stringBuilder.substring(i,i+8);//截取8位
            }
            //将strByte 转成一个byte,放入到haffumanCodesMapBytes
            //Integer.parseInt(strByte,2):将一个字符串如1001 1001 按照二进制转成int
            //string-->int -->byte然后放入byte数组中
            haffumanCodesBytes[index] = (byte)Integer.parseInt(strByte,2);
            index++;
        }
        return haffumanCodesBytes;
    }

    //-------------------------数据的解码-----------------------------------------

    /**
     *
     * @param b 传入的一个字节byte
     * @param flag:标识是否需要补高位,如果是true,表示需要补高位,false表示不补
     * @return :传入的字节 对应的二进制 字符串(注意是按补码返回)
     */
    private static String byteToBitString(boolean flag,byte b){
        //使用变量保存b
        int temp = b;//将b转成int,因为Integer.toBinaryString(int i)参数是int
        //如果是正数,我们还需要补高位
        if(flag){
            temp |= 256;//按位与 256,如1 0000 0000 | 0000 0001 -->1 0000 0001
        }
        //返回的是temp对应的二进制的补码
        String str = Integer.toBinaryString(temp);
        if(flag){
            return str.substring(str.length() -8);
        }else{
            return str;
        }
    }
    //解压的方法
    private static byte[] decode(
                                 byte[] haffumanCodesBytes,
                                 Map<Byte,String> huffmanCodesMap){
        //1.先得到haffumanCodesBytes 对应的二进制字符串,形式10101000...
        StringBuilder stringBuilder = new StringBuilder();
        //将byte数组转成 二进制 的字符串
        for (int i = 0; i < haffumanCodesBytes.length; i++) {
            byte b = haffumanCodesBytes[i];//取出每一个字节
            //判断是不是最后一个字节
            boolean flag = (i == haffumanCodesBytes.length - 1);
            //不是最后一个字节,追加
            stringBuilder.append(byteToBitString(!flag,b));
        }
        System.out.println("hafuman字节数组对应的二进制字符串:"+
                stringBuilder.toString());
        //把字符串 按照指定的哈夫曼编码表 进行解码
        //把哈夫曼编码表 进行调换,因为要反向查询 a->100 ,100->a
        HashMap<String, Byte> map = new HashMap<>();//反向的存储结构
        for (Map.Entry<Byte, String> entry : huffmanCodesMap.entrySet()) {
            map.put(entry.getValue(),entry.getKey());
        }
        System.out.println("map="+map);
        //创建 给定集合,存放byte
        ArrayList<Byte> list = new ArrayList<>();
        //i 可以理解为索引,扫描stringBuilder
        for (int i = 0; i < stringBuilder.length(); ) {
            int count = 1;//小的计数器
            boolean flag = true;
            Byte b = null;
            while(flag){
                //i 不动哦那个,让count移动,指定匹配到一个字符
                //递增的取出 key 1
                String key = stringBuilder.substring(i, i + count);
                b = map.get(key);
                if(b == null){//说明没有匹配到
                    count++;
                }else{//匹配到
                    flag = false;
                }
            }
            list.add(b);
            i+= count;//i直接移动到count
        }
        //当for循环结束后,我们list中就存放了所有的字符"1 like like like..."
        byte[] b = new byte[list.size()];
        for (int i = 0; i < b.length; i++) {
            //把list中的数据 放入到byte[] 并返回
            b[i] = list.get(i);
        }
        return b;
    }
    //文件的压缩方法
    public static void zipFile(String srcFile,String desFile){
        //创建输出流
        OutputStream os = null;
        //创建对象输出流
        ObjectOutputStream oos = null;
        //创建文件的输入流
        FileInputStream is = null;
        try{
            //创建文件的输入流
            is = new FileInputStream(srcFile);
            //创建一个和源文件大小一样的byte[],将源文件转成字节数组
            byte[] b = new byte[is.available()];
            //读取文件,读取源文件的字节数组
            is.read(b);
            //直接对源文件压缩
            byte[] huffmanBytes = huffmanZip(b);//压缩字节数组
            //创建文件的输出流,存放压缩文件
            os = new FileOutputStream(desFile);
            oos = new ObjectOutputStream(os);
            //1.把哈夫曼编码后的字节数组 写入到压缩文件
            oos.writeObject(huffmanBytes);
            //2.再写入一个编码表对象
            oos.writeObject(haffumanCodesMap);
        }catch (Exception e){
            try {
                is.close();
                oos.close();
                os.close();
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
    }
    //文件的解压

    /**
     *
     * @param zipFile 准备解压的文件
     * @param desFile 将文件解压到哪个路径
     */
    public static void unZip(String zipFile,String desFile){
        //定义文件输入流
        InputStream is =null;
        //定义一个对象输入流
        ObjectInputStream ois = null;
        //定义文件的输出流
        OutputStream os = null;
        try {
            //创建文件输入流
            is= new FileInputStream(zipFile);
            //创建一个和is关联的对象输入流
             ois = new ObjectInputStream(is);
             //1.读取byte数组,即压缩后的字节数组
            byte[] huffmanBytes = (byte[])ois.readObject();
            //2.再读取哈夫曼编码表97-->001等
            Map<Byte,String> huffmanCodesMap =
                    (Map<Byte,String>) ois.readObject();
            //解码
            byte[] bytes = decode(huffmanBytes,huffmanCodesMap);
            //将bytes 数组写入到 目标文件
             os = new FileOutputStream(desFile);
             //写数据到 desFile,os.write(byte b[])
            os.write(bytes);
        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally {
            try {
                os.close();
                ois.close();
                is.close();
            }catch (Exception ex){
                System.out.println(ex.getMessage());
            }
        }

    }

}

//一、节点类
class Node<T> implements Comparable<Node<T>>{
    T data;//存放的数据(如字符,a--97)
    int weight;//权值
    Node<T> left;//左子树节点
    Node<T> right;//右子树节点

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

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

    @Override
    public int compareTo(Node o) {
        //从小到大排序
        return this.weight - o.weight;
    }
    //前序遍历
    public void preOrder(){
        //根,左右
        //根:输出当前节点this,即正在调用的节点
        System.out.println(this);
        //左:当前节点的左子树不为空,继续向下遍历
        if(this.left != null){
            this.left.preOrder();
        }
        //左:当前节点的右子树不为空,继续向下遍历
        if(this.right != null){
            this.right.preOrder();
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值