基于哈夫曼树实现的压缩软件

1.哈夫曼编码原理

哈夫曼编码是由哈弗曼树得到的编码。哈夫曼树属于二叉树,即树的结点最多拥有2个孩子结点。若该二叉树带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

哈夫曼编码压缩的原理就是将出现频率较高的字母用更少的空间表示,从而实现压缩的效果。

2.构建哈夫曼树

构造哈夫曼编码即构造哈弗曼树。其中每个树的结点含有对应的数据和权重。为了构造哈夫曼树还要实现Comparable接口,方便后面的排序。

public class HuffmenNode implements Comparable<HuffmenNode> {
    public HuffmenNode left;
    public HuffmenNode right;
    public HuffmenNode parent;
    public int data; 
    private byte weight;
}

之后构树的具体步骤为:

1.将结点按字母出现频率从小到大排序。对每个字母都创建一个结点,

2. 每次取出当前集合中出现频率最低的两个字母,生成他们的父节点,父节点的频率为两字母频率之和

3.删除掉集合中的两个子节点,并将父节点加入到集合中。

4.重复步骤1-3,直到集合中剩下最后一个节点,即为哈夫曼树的根节点。

//根据节点列表 创建哈夫曼树
    private static HuffmenNode createHuffmenTree(List<HuffmenNode> nodeList) {
        int length = nodeList.size();
        while (length > 1) {
            HuffmenNode huffmenNode01 = nodeList.get(length - 1);
            HuffmenNode huffmenNode02 = nodeList.get(length - 2);
            HuffmenNode huffmenNodeNew = new HuffmenNode(null, huffmenNode01.weight + huffmenNode02.weight);
            huffmenNodeNew.leftNode = huffmenNode01;
            huffmenNodeNew.rightNode = huffmenNode02;
            nodeList.remove(huffmenNode01);
            nodeList.remove(huffmenNode02);
            nodeList.add(huffmenNodeNew);
            Collections.sort(nodeList);
            length = nodeList.size();
        }
        return nodeList.get(0);
    }

3.根据哈弗曼树获得对应的编码

从根节点开始遍历整棵树,获得每个节点对应的哈夫曼编码。将字母对应的字节和其编码存入哈希表中。

private static void getCodes(HuffmenNode node, String s, StringBuffer sb) {
        StringBuffer tempSb = new StringBuffer(sb);
        tempSb.append(s);
        if (node.data == null) {
            getCodes(node.leftNode, "0", tempSb);
            getCodes(node.rightNode, "1", tempSb);
        } else {
            mapCode.put(node.data, tempSb.toString());
        }
    }

4.将对应的文件进行压缩

传入的参数为目标字节数组和对应的哈夫曼编码表。返回的值为哈夫曼编码表对应的字节数组。

由于经过哈夫曼编码后获得的二进制字符串可能不再可以被8整除,在结尾时采用了低位补0的方式

 private static byte[] encodeByHuffmenCode(byte[] bytes, Map<Byte, String> huffmenCodeMap) {
        //将bytes转换成二进制字符串
        StringBuffer sb = new StringBuffer();
        for (byte b : bytes) {
            String str = huffmenCodeMap.get(b);
            sb.append(str);
        }
//        System.out.println(sb.toString());
        //将二进制字符串转变为处理后的byte
        int len = sb.length();
        int newLenght = (len % 8 == 0) ? (len / 8) : (len / 8 + 1);
        byte[] targetBytes = new byte[newLenght];
        for (int i = 0; i < targetBytes.length; i++) {
            if ((i + 1) * 8 > len) {
                targetBytes[i] = (byte) Integer.parseInt(sb.substring(i * 8), 2);
            } else {
                targetBytes[i] = (byte) Integer.parseInt(sb.substring(i * 8, (i + 1) * 8), 2);
            }
        }
        return targetBytes;
    }

5.存储压缩后的文件。

利用序列化存储的方式,将文件对应的哈夫曼编码表和文件压缩字节数组存入新文件中。

对于序列化(ObjectOutputStream/ObjectInputStream):存入的顺序和读出的顺序要保证一样,同时读取时要进行指明类型的转化.

一个自己写的例子:

public static void main(String[] args) {
        byte[] byarray = {10,20,30,40};
        String s = "你好我是周杰伦";
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        try {
            OutputStream out = new FileOutputStream("C:\\Users\\ZDX\\Desktop\\新建文件夹\\1110.txt");
            ObjectOutputStream obout = new ObjectOutputStream(out);
            obout.writeObject(byarray);
            obout.writeObject(s);
            obout.writeObject(list);
        }  catch (Exception e) {
            e.printStackTrace();
        }

    }

 读:

public static void main(String[] args)  {
        try {
            InputStream input = new FileInputStream("C:\\Users\\ZDX\\Desktop\\新建文件夹\\1110.txt");
            ObjectInputStream objinput = new ObjectInputStream(input);
            //按顺序读
            byte[] byarray = (byte[]) objinput.readObject();
            String s = (String) objinput.readObject();
            List<Integer> list = (List<Integer>) objinput.readObject();
            System.out.println(s);
            for (Integer i :list) {
                System.out.print(i+ " ");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

结果为: 

 6.解压文件:

其具体为:

1.首先从文件中读取出编码表和目标字节数组。

2.完成转换:字节数组→二进制字符串

3.根据哈夫曼编码,读取二进制字符串,得到原文件。

由字符串到字节列表的转化:遍历字符串,一旦发现匹配的编码则输出。

private static List<Byte> getSource(String codeStr, Map<String, Byte> byteMap) {
        List<Byte> tempList = new ArrayList<>();
        for (int i = 0; i < codeStr.length(); ) {
            int count = 1;
            boolean flag = true;
            Byte b = null;
            while (flag) {
                String key = codeStr.substring(i, i + count);
                b = byteMap.get(key);
                if (b == null) {
                    count++;
                } else {
                    flag = false;
                }
            }
            tempList.add(b);
            i += count;
        }
        return tempList;
    }

注意:对于最后一个补0得到的字节的处理:

将原字节首先用转化成int型(32位)再与256进行或运算,然后截取最后八位的数据,从而得到元数据。

private static String byteToString(byte target) {
        int temp = target;
        temp |= 256;
        String str = Integer.toBinaryString(temp);
        return str.substring(str.length() - 8);
    }

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一个非常有趣的问题!我会尽力回答你。 Huffman编码是一种基于字符频率的无损数据压缩算法,它通过将常用字符用短码表示,而将不常用字符用长码表示,从而实现数据压缩的目的。Huffman编码的核心是构建Huffman树,该树是一种树形结构,其中每个叶子节点对应着一个字符,而非叶子节点则对应着字符出现的频率。 下面是如何利用Huffman树和Huffman编码实现无损数据压缩的步骤: 1. 统计每个字符出现的频率,并将它们存储在一个字符频率表中。 2. 根据字符频率表构建Huffman树。具体构建方法是: a. 将所有字符看作叶子节点,并将它们按照频率从小到大排序。 b. 每次取出频率最小的两个叶子节点,将它们作为左右子节点构建一个新的节点,该节点的频率为两个叶子节点的频率之和。 c. 将新的节点插入到原来的节点序列中,并重新按照频率从小到大排序。 d. 重复步骤b和c,直到所有节点都被合并成一个根节点为止。此时,Huffman树就构建完成了。 3. 根据Huffman树生成Huffman编码。具体生成方法是: a. 从根节点开始遍历Huffman树。 b. 当遍历到一个左子节点时,在编码序列的末尾添加一个0;当遍历到一个右子节点时,在编码序列的末尾添加一个1。 c. 当遍历到一个叶子节点时,将该节点的字符和对应的编码存储到编码表中。 4. 将原始数据按照编码表进行编码,并将编码后的数据存储到文件中。编码后的数据长度通常比原始数据长度要短。 5. 解压缩时,读取编码表和编码后的数据,并根据编码表将编码后的数据进行解码,得到原始数据。 至此,利用Huffman树和Huffman编码实现无损数据压缩就完成了。当然,实现一个完整的无损数据压缩软件工具还需要考虑很多细节问题,比如如何存储编码表和编码后的数据,如何处理文件头和文件尾等等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值