JAVA代码编写哈夫曼编码实现数据和文件的压缩和解压算法

2023-11-03

压缩算法思路:
1.将待压缩的字符串变成字节数组 byte[] contentBytes;

2.将字节数组每个字符出现的次数统计出来变为Node类(value为字符对应的Ascci码,weight为字符出现的次数也是哈夫曼树的权值),存入List集合中方便下面构建哈夫曼树;

 List<Node> nodes = new ArrayList<>();
        //遍历bytes,记录每个value出现的次数,出现的次数就是huffman树的权值
        Map<Byte, Integer> counts = new HashMap<>();
        for (byte b : bytes) {
            Integer count = counts.get(b);
            if (count == null) {
                counts.put(b, 1);
            } else {
                counts.put(b, count + 1);
            }
        }
        for (Map.Entry<Byte, Integer> map : counts.entrySet()) {
            nodes.add(new Node(map.getKey(), map.getValue()));
        }
        return nodes;

3.构建哈弗曼树, private static Map<Byte, String> huffmanCode = new HashMap<Byte, String>(); 用huffmanCode存放对应字符的Ascci码,和对应路径 (构建哈夫曼树的过程请看上篇文章)

private static Node getHuffman(List<Node> nodes) {
        while (nodes.size() > 1) {
            Collections.sort(nodes);
            Node leftNode = nodes.get(0);
            Node rightNode = nodes.get(1);
            Node parent = new Node(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.right = rightNode;
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);
        }
        return nodes.get(0);
    }

4.遍历哈弗曼树得到字符的二进制路径,从根节点出发,往左为0,往右为1直到Node.value!=null,说明找到了对应字符

 //得到所有字符的路径
    private static void getAllCodes(Node root) {
        if (root == null) {
            return;
        }
        //记录路径,遍历huffman树时往左为0,往右为1直到找到对应字符;
        StringBuilder stringBuilder = new StringBuilder();
        getcode(root.left, "0", stringBuilder);
        getcode(root.right, "1", stringBuilder);

    }


 /*
     * @param node 当前需要遍历的节点
     * @param code 值为"0"或者"1"代表向左或者向右
     * @param stringBuilder
     * @return : void
     * @date : 2020/3/8 13:20
     */
    private static void getcode(Node node, String code, StringBuilder stringBuilder) {
        StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
        stringBuilder2.append(code);
        if (node != null) {
            if (node.value == null) {//证明这个点点为非叶子节点
                //向左递归
                getcode(node.left, "0", stringBuilder2);
                //向右递归
                getcode(node.right, "1", stringBuilder2);

            } else {
                huffmanCode.put(node.value, stringBuilder2.toString());
            }
        }
    }

5.将按照contentBytes的顺序逐个用二进制路径代替字符形成一个Stringbuiled字符串

private static byte[] getZipArr(byte[] bytes, Map<Byte, String> huffmanCode) {
        //按照bytes中的字符排序顺序用路径替代字符存入stringBuilder
        StringBuilder stringBuilder = new StringBuilder();
        for (byte b : bytes) {
            stringBuilder.append(huffmanCode.get(b));
        }
        //将stringBuilder每8个二进制当做一个byte类型存入字节数组
        int len = 0;
        //用于补齐不足8位的地方
        String str = "";
        //确定字节数组长度
        if (stringBuilder.length() % 8 == 0) {
            len = stringBuilder.length() / 8;
        } else {
            len = stringBuilder.length() / 8 + 1;
            //不足8位的地方需要补0的个数
            zeloCount = 8 - (stringBuilder.length() % 8);
            for (int i = 0; i < zeloCount; i++) {
                str += "0";
              //  System.out.println("zelo:"+str);
            }
            stringBuilder.append(str);
        }
       // System.out.println("stringBuilder1:"+stringBuilder);
        //将stringbuild里面的二进制路径字符串每8位进行翻译成十进制
        return stringTransfertoByteArr(stringBuilder, len);
    }


6.将Stringbuiled按照每8位2进制路径转为一个十进制的数存在字节数组中;

 //将stringbuild里面的二进制路径字符串每8位进行翻译成十进制
    private static byte[] stringTransfertoByteArr(StringBuilder stringBuilder, int len) {
        byte[] huffmanCodeArr = new byte[len];
        //huffmanCodeArr的索引
        int index = 0;
        for (int i = 0; i < stringBuilder.length(); i += 8) {
            if (i + 8 > stringBuilder.length()) {//不够8位
                huffmanCodeArr[index] = (byte) Integer.parseInt(stringBuilder.substring(i), 2);
            } else {
                huffmanCodeArr[index] = (byte) Integer.parseInt(stringBuilder.substring(i, i + 8), 2);
            }
            index++;
        }
        return huffmanCodeArr;
    }

解压算法思路:

7.将存放十进制的字节数组转变为它对应的二进制路径,然后用Stringbuild汇总这些二进制就可以得到5中的Stringbuild;

 //将压缩后的字节数组变为用二进制路径表示字符的字符串
    private static StringBuilder byteArrtransfertoString(byte[] bytes) {
        StringBuilder stringBuilder = new StringBuilder();
        //将byte数组转化为二进制字符串
        for (int i = 0; i < bytes.length; i++) {
            byte b = bytes[i];
            boolean flag = true;
            String str = byteTransfertoBinaryString(b);
            if (i == bytes.length - 1) {
             //   System.out.println("str1:"+str);
                str = str.substring(0, 8-zeloCount);
             //   System.out.println("str2:"+str);
            }
            stringBuilder.append(str);
        }
        //System.out.println("stringBuilder2:"+stringBuilder);
        return stringBuilder;
    }
    
//将byte转化为二进制存入String
    private static String byteTransfertoBinaryString(byte b) {
        int temp = b;
        temp |= 256;
        String str = Integer.toBinaryString(temp);
        int len = str.length();
        return str.substring(len - 8);

    }

8.然后将huffmanCode反转变为Map<Byte, String> map= new HashMap<Byte, String>(),然后遍历7中的Stringbuild如果map中存在Stringbuild中某一段的路径就将它对应的字符添加到一个list中,遍历完成后就将list赋予给一个新的字节数组,打印该数组就得到了原来的字符串数组;


//遍历7中的Stringbuild如果map中存在Stringbuild中某一段的路径就添加到一个list中
private static List getChar(Map<String, Byte> map, StringBuilder stringBuilder) {
        List<Byte> list = new ArrayList<Byte>();
        for (int i = 0; i < stringBuilder.length(); ) {
            int count = 1;
            boolean flag = true;
            //取出路径对应的字符
            while (flag) {
                if (!map.containsKey(stringBuilder.substring(i, i + count))) {
                    count++;
                } else {
                    flag = false;
                }
            }
            list.add(map.get(stringBuilder.substring(i, i + count)));
            i += count;
        }
        return list;
    }

private static byte[] decode(byte[] bytes, Map<Byte, String> huffmanCode) {
        //将压缩后的字节数组变为用二进制路径表示字符的字符串,然后将字符串中路径变成对应字符
        StringBuilder stringBuilder = byteArrtransfertoString(bytes);
        Map<String, Byte> map = new HashMap<String, Byte>();
        //将huffmanCode反转,'a'->101 编程101->'a',将路径转为对应的字符
        for (Map.Entry<Byte, String> entry : huffmanCode.entrySet()) {
            map.put(entry.getValue(), entry.getKey());
        }
        //将路径翻译成对应的字符
        List<Byte> list = getChar(map, stringBuilder);
        //将list变为数组
        byte[] contenArr = new byte[list.size()];
        for (int i = 0; i < list.size(); i++) {
            contenArr[i] = list.get(i);
        }
        return contenArr;
    }

完整代码如下:

package com.yg.tree.huffman;/*
@author  Mu_Mu
@date    2020/3/8  10:16
*/

import com.yg.tree.HuffmanTree;

import java.io.*;
import java.util.*;

public class HaffmanCode {
    //存放对应字符的Ascci码,和对应路径
    private static Map<Byte, String> huffmanCode = new HashMap<Byte, String>();
    //补全最后一位需要加0的个数
    private static Integer zeloCount = 0;

    public static void main(String[] args) {
        //文件压缩
        String src = "D:/1.png";
        String dst = "D:/src.zip";
        zipFile(src, dst);
        System.out.println("压缩完成!");

        //解压
        String zipFile = "D:/src.zip";
        String dstFile = "D:/2.png";
        unZipFile(zipFile, dstFile);
        System.out.println("解压成功!");

//        String content = "i like java do you like a java very much";
//        byte[] contentBytes = content.getBytes();
//        //数据压缩
//        byte[] zipArr = huffmanZip(contentBytes);
//        System.out.println(Arrays.toString(zipArr));
//
//        //数据解压
//        byte[] decodeArr = decode(zipArr, huffmanCode);
//         System.out.println(new String(decodeArr).toString());

    }

    //压缩文件
    private static void zipFile(String src, String dst) {
        InputStream is = null;
        OutputStream os = null;
        ObjectOutputStream oos = null;
        try {
            is = new FileInputStream(src);
            //创建一个和文件一样大的字节数组
            byte[] b = new byte[is.available()];
            //将文件以字节流的形式读到字节数组中
            is.read(b);
            byte[] huffmanCodeArr = huffmanZip(b);
            os = new FileOutputStream(dst);
            oos = new ObjectOutputStream(os);
            //将字节数组,和哈夫曼编码以对象的形式输出去
            oos.writeObject(huffmanCodeArr);
            oos.writeObject(huffmanCode);
            //将字节数组最后一位要补0的个数传过去
            oos.writeObject(zeloCount);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                os.close();
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }

    //解压文件
    /*
     * @param zipFile 准备解压的文件
     * @param dstFile 将文件解压的路径
     * @return : void
     * @date : 2020/3/10 12:18
     */
    private static void unZipFile(String zipFile, String dstFile) {
        InputStream is = null;
        ObjectInputStream ois = null;
        OutputStream os = null;
        try {
            is = new FileInputStream(zipFile);
            ois = new ObjectInputStream(is);
            //readObject()是按照你存入时的顺序依次读取对象
            byte[] huffmanCodeArr = (byte[]) ois.readObject();
            huffmanCode = (Map<Byte, String>) ois.readObject();
            zeloCount = (Integer) ois.readObject();
           // System.out.println("zeloCount:"+zeloCount);
            byte[] contentArr = decode(huffmanCodeArr, huffmanCode);
            os=new FileOutputStream(dstFile);
            os.write(contentArr);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                ois.close();
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //解码
    /*
     * @param bytes  压缩后的字节数组
     * @param huffmanCode 字符,和字符对应的路径组成的map
     * @return : byte[] 返回元数组
     * @date : 2020/3/9 14:26
     */
    private static byte[] decode(byte[] bytes, Map<Byte, String> huffmanCode) {
        //将压缩后的字节数组变为用二进制路径表示字符的字符串,然后将字符串中路径变成对应字符
        StringBuilder stringBuilder = byteArrtransfertoString(bytes);
        Map<String, Byte> map = new HashMap<String, Byte>();
        //将huffmanCode反转,'a'->101 编程101->'a',将路径转为对应的字符
        for (Map.Entry<Byte, String> entry : huffmanCode.entrySet()) {
            map.put(entry.getValue(), entry.getKey());
        }
        //将路径翻译成对应的字符
        List<Byte> list = getChar(map, stringBuilder);
        //将list变为数组
        byte[] contenArr = new byte[list.size()];
        for (int i = 0; i < list.size(); i++) {
            contenArr[i] = list.get(i);
        }
        return contenArr;
    }

    //将压缩后的字节数组变为用二进制路径表示字符的字符串
    private static StringBuilder byteArrtransfertoString(byte[] bytes) {
        StringBuilder stringBuilder = new StringBuilder();
        //将byte数组转化为二进制字符串
        for (int i = 0; i < bytes.length; i++) {
            byte b = bytes[i];
            boolean flag = true;
            String str = byteTransfertoBinaryString(b);
            if (i == bytes.length - 1) {
             //   System.out.println("str1:"+str);
                str = str.substring(0, 8-zeloCount);
             //   System.out.println("str2:"+str);
            }
            stringBuilder.append(str);
        }
        //System.out.println("stringBuilder2:"+stringBuilder);
        return stringBuilder;
    }

    private static List getChar(Map<String, Byte> map, StringBuilder stringBuilder) {
        List<Byte> list = new ArrayList<Byte>();
        for (int i = 0; i < stringBuilder.length(); ) {
            int count = 1;
            boolean flag = true;
            //取出路径对应的字符
            while (flag) {
                if (!map.containsKey(stringBuilder.substring(i, i + count))) {
                    count++;
                } else {
                    flag = false;
                }
            }
            list.add(map.get(stringBuilder.substring(i, i + count)));
            i += count;
        }
        return list;
    }

    //将byte转化为二进制存入String
    private static String byteTransfertoBinaryString(byte b) {
        int temp = b;
        temp |= 256;
        String str = Integer.toBinaryString(temp);
        int len = str.length();
        return str.substring(len - 8);

    }

    //将所有步骤封装
    /*
     * @param bytes 原字符串对应的byte数组
     * @return : byte[] 压缩完后后将要发送的数组
     * @date : 2020/3/9 12:30
     */
    private static byte[] huffmanZip(byte[] bytes) {
        //现将数组变为装有node节点的list集合
        List<Node> nodes = getNodeList(bytes);
        //得到对应的huffman树
        Node root = getHuffman(nodes);
        getAllCodes(root);
        byte[] huffmanZipArr = getZipArr(bytes, huffmanCode);
        return huffmanZipArr;
    }

    //得到压缩后的字节数组
    /*
     * @param bytes 原字符串对应的byte数组,按照这个数组字符排列顺序打印字符对应的路径
     * @param huffmanCode 存放字符,和字符路径的map
     * @return : byte[] 将所有路劲汇总每8个当成一位
     * @date : 2020/3/9 12:07
     */
    private static byte[] getZipArr(byte[] bytes, Map<Byte, String> huffmanCode) {
        //按照bytes中的字符排序顺序用路径替代字符存入stringBuilder
        StringBuilder stringBuilder = new StringBuilder();
        for (byte b : bytes) {
            stringBuilder.append(huffmanCode.get(b));
        }
        //将stringBuilder每8个二进制当做一个byte类型存入字节数组
        int len = 0;
        //用于补齐不足8位的地方
        String str = "";
        //确定字节数组长度
        if (stringBuilder.length() % 8 == 0) {
            len = stringBuilder.length() / 8;
        } else {
            len = stringBuilder.length() / 8 + 1;
            //不足8位的地方需要补0的个数
            zeloCount = 8 - (stringBuilder.length() % 8);
            for (int i = 0; i < zeloCount; i++) {
                str += "0";
              //  System.out.println("zelo:"+str);
            }
            stringBuilder.append(str);
        }
       // System.out.println("stringBuilder1:"+stringBuilder);
        //将stringbuild里面的二进制路径字符串每8位进行翻译成十进制
        return stringTransfertoByteArr(stringBuilder, len);
    }

    //将stringbuild里面的二进制路径字符串每8位进行翻译成十进制
    private static byte[] stringTransfertoByteArr(StringBuilder stringBuilder, int len) {
        byte[] huffmanCodeArr = new byte[len];
        //huffmanCodeArr的索引
        int index = 0;
        for (int i = 0; i < stringBuilder.length(); i += 8) {
            if (i + 8 > stringBuilder.length()) {//不够8位
                huffmanCodeArr[index] = (byte) Integer.parseInt(stringBuilder.substring(i), 2);
            } else {
                huffmanCodeArr[index] = (byte) Integer.parseInt(stringBuilder.substring(i, i + 8), 2);
            }
            index++;
        }
        return huffmanCodeArr;
    }

    //得到所有字符的路径
    private static void getAllCodes(Node root) {
        if (root == null) {
            return;
        }
        //记录路径,遍历huffman树时往左为0,往右为1直到找到对应字符;
        StringBuilder stringBuilder = new StringBuilder();
        getcode(root.left, "0", stringBuilder);
        getcode(root.right, "1", stringBuilder);

    }

    /*
     * @param node 当前需要遍历的节点
     * @param code 值为"0"或者"1"代表向左或者向右
     * @param stringBuilder
     * @return : void
     * @date : 2020/3/8 13:20
     */
    private static void getcode(Node node, String code, StringBuilder stringBuilder) {
        StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
        stringBuilder2.append(code);
        if (node != null) {
            if (node.value == null) {//证明这个点点为非叶子节点
                //向左递归
                getcode(node.left, "0", stringBuilder2);
                //向右递归
                getcode(node.right, "1", stringBuilder2);

            } else {
                huffmanCode.put(node.value, stringBuilder2.toString());
            }
        }
    }

    private static Node getHuffman(List<Node> nodes) {
        while (nodes.size() > 1) {
            Collections.sort(nodes);
            Node leftNode = nodes.get(0);
            Node rightNode = nodes.get(1);
            Node parent = new Node(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.right = rightNode;
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);
        }
        return nodes.get(0);
    }

    //将数组变为List<Node>
    private static List<Node> getNodeList(byte[] bytes) {
        List<Node> nodes = new ArrayList<>();
        //遍历bytes,记录每个value出现的次数,出现的次数就是huffman树的权值
        Map<Byte, Integer> counts = new HashMap<>();
        for (byte b : bytes) {
            Integer count = counts.get(b);
            if (count == null) {
                counts.put(b, 1);
            } else {
                counts.put(b, count + 1);
            }
        }
        for (Map.Entry<Byte, Integer> map : counts.entrySet()) {
            nodes.add(new Node(map.getKey(), map.getValue()));
        }
        return nodes;
    }

    //前序遍历
    private static void preOrder(Node root) {
        if (root == null) {
            return;
        }
        root.preOrder();
    }

}

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

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

    }

    //前序遍历
    public void preOrder() {
        System.out.println(this);
        if (this.left != null) {
            this.left.preOrder();
        }
        if (this.right != null) {
            this.right.preOrder();
        }
    }

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

    @Override
    public int compareTo(Node o) {
        return this.weight - o.weight;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVA代码编写哈夫曼编码实现数据和文件的压缩和解压算法 的相关文章

  • antV/g2的使用

    antV g2 特点 以数据驱动 安装 npm instal antv g2 使用 准备一个容器 div div 执行代码 1 引入 import
  • 如何使用C ++以编程方式在Word文档中使用目录?

    目录 TOC 是Word文档的重要组成部分 它提供了文档内容的概述 并允许您快速导航到所需的部分 您可能会遇到需要以编程方式从Word文档中添加 提取 更新或删除目录的情况 为此 本文将教您如何使用C 处理Word文件中的目录 让我们探索以

随机推荐

  • D. Permutation Restoration(优先队列+贪心)

    Problem D Codeforces include
  • 学习Flask之分页插件flask_bootstrap

    这次分页功能 主要是依靠 Flask Bootstrap 首先也是下载flask bootstrap pip install flask bootstrap 安装完后可以观察里面的文件夹 里面其实还有nav 导航 form 表单 pagin
  • linux内核调试环境的搭建(使用qemu)

    这里说明下 本人调试的内核版本是2 6 11 12 为什么去调试这么 古老 的版本 原因不多说了 你手头也许正拿着ULK3 而它针对的内核版本正是2 6 11 有比这更好的理由吗 而且这个版本不算旧 已不算新 我认为还算不错 想想当下还有如
  • 我的计算机管理里面没有家庭组,Win10控制面板没有家庭组怎么解决?

    我们经常在使用电脑的时候经常会用到家庭组这个功能 家庭组使用起来非常方便的功能 但是最近很多的用户们反映Win10家庭组功能在控制面板找不到了 这个问题我们要怎么解决呢 下面小编为大家带来详细的解决教程介绍 快来看看吧 Win10控制面板没
  • 七牛云 composer 文件上传、删除、请除缓存操作

    class QiniuUp extends ModelBasic private AccessKey private SecretKey private bucket private auth function construct pare
  • DBCP连接池参数

    DBCP连接池参数说明如下 1 maxActive 10 表示并发情况下最大可从连接池中获取的连接数 2 maxIdle 5 如果在并发时达到了maxActive 10 那么连接池就必须从数据库中获取10个连接来供应用程序使用 当应用程序关
  • [1187]win环境2026, SSL connection error: unknown error number

    先贴上详细的报错信息 PS D test orchard liang gt python manage py sqlmigrate app 0001 Traceback most recent call last File D Python
  • python复习

    python复习 文章目录 python复习 一 Python基础笔试题 1 什么是变量 2 python和CPython是什么关系 3 如何生成一个整数序列 4 表达式与语句的主要区别是什么 5 Python的基础数据类型有哪些 6 在P
  • [RK3568][Android11]内核Oops日志分析

    文章目录 一 什么是内核oops 二 内核oops信息 三 工具调试内核oops 3 1 gdb list command 3 2 addr2line 3 3 objdump 一 什么是内核oops Linux内核在发生kernel pan
  • 如何在Linux下开发

    ctrl alt t打开命令行 输入vi 文件名 c 一 VI的使用 VI有两种模式 一种是命令行模式 一种是输入模式 命令行模式 这个是默认模式 VI之后就是这个模式 如果我们要从输入模式回到命令行模式 按下esc 待INSERT消失 我
  • 解决ubuntu不能远程连接

    1 查看当前是否安装了ssh server服务 dpkg l grep ssh 2 安装ssh server服务 sudo apt get install openssh server
  • 将你的STM32搞成Arduino(一)

    接触STM32有一年半了从刚开是的懵懂无知到现在的拉个库就是干 我慢慢的发现STM3功能的强大已经配套环境的完整程序 他不像是51单片机那样已经被intel抛弃 之后也没人出一个官方的库 一切都是纯生的需要自己搭建 STM32标准库已经为你
  • Java接口关系树状图

    圆形 接口 矩形 表示对象 向上的蓝色箭头 接口继承关系 乡下的蓝色箭头 依赖关系
  • 正则知识点滴

    s S 与 的区别 s S 支持跨行匹配
  • Ubuntu下使用ls命令显示文件颜色相关内容及修改

    lt 转载自 http pcyoyo com p 465 gt 在Ubuntu下 使用ls命令显示目录下文件及文件夹时会先显示不同颜色 如下图所示 如果知道了不同颜色分别代表的含义 那么对于我们查看目录下文件信息方便了很多 所以就搜索了一下
  • css实现:after中使用图片

    先看一下效果 下面是代码实现 xin position relative font size 20rpx color 15bf5d border 1rpx dashed ccc padding top 20rpx xin after con
  • pg常用插件

    pg软件包自带插件 前言 pg的插件是基于库的 pg的数据字典介绍 1 pg stat statements插件 Pg stat statements 是一个扩展 而不是核心数据库的一部分 它是一个contrib 扩展 随 postgres
  • 推荐一个冷门又逆天的副业(Python兼职可月入15k+)

    最近在论坛上看到一个测试 特扎心 以下三种情况 哪个让你最绝望 发薪日开心三分钟 各种家庭花销和贷款过一遍立马所剩无几 被领导骂到哭 因为没钱不敢裸辞 公司业绩不好 自己更是活成了一个小透明 薪资拿的少 还要随时担心被新人优化 说实话 我真
  • DirectX11 简介+环境配置

    文章目录 前言 获取SDK 安装 项目环境配置 创建项目 链接库 方法一 方法二 前言 DX11是Win7的产物 它是09年发布的 可谓是非常古老 那么为什么我们还要学习呢 这是为了给下一步的DX12做准备 如果你是Win10用户 且安装了
  • JAVA代码编写哈夫曼编码实现数据和文件的压缩和解压算法

    压缩算法思路 1 将待压缩的字符串变成字节数组 byte contentBytes 2 将字节数组每个字符出现的次数统计出来变为Node类 value为字符对应的Ascci码 weight为字符出现的次数也是哈夫曼树的权值 存入List集合