【算法】java实现用哈夫曼编码对文件进行压缩和解压

7 篇文章 0 订阅
6 篇文章 0 订阅

1、什么是哈夫曼编码?

  • 哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式,可变字长编码(VLC)的一种。
  • Huffman于1952年提出的一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫做Huffman编码(有时也称为霍夫曼编码)。
  • 哈夫曼编码是一种高效编码方式,在信息存储和传输过程中,可以用于信息压缩。

2、哈夫曼编码如何对信息进行压缩?

  • 编码的方式可以有很多种,比如:ASCII码。

在这里插入图片描述

  • 显然,ASCII码是一种等长编码,任何字符的编码长度都相等。
  • 哈夫曼编码(Huffman Coding),是可变字长且总长度最小的。
  • 所以可以利用哈夫曼编码信息编码的总长度最小的特点对等长编码的信息进行压缩。

3、哈夫曼编码如何生成

  • 假如一段信息里只有A,B,C,D,E,F这6个字符,他们出现的次数依次是2次,3次,7次,9次,18次,25次。
  • 然后把这6个字符当做6个叶子结点,把字符出现次数当做结点的权重,以此来生成一颗哈夫曼树

在这里插入图片描述

  • 再然后,把哈夫曼树的每一个结点包括左、右两个分支与二进制的0、1两种状态对应起来,结点的左分支当做0,结点的右分支当做1

在这里插入图片描述

  • 这样一来,从哈夫曼树的根结点到每一个叶子结点的路径,都可以等价为一段二进制编码。

在这里插入图片描述

  • 利用哈夫曼树特性所生成的二进制编码,实现了1.任何一个字符编码,都不是其他字符编码的前缀。 2.信息编码的总长度最小。 两个重点目标。
  • 如此同样的一段信息,采用哈夫曼编码会比等长编码占用更少的存储空间。

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

4、java实现用哈夫曼编码对文件进行压缩和解压

  • 哈夫曼树结点
package com.data.algorithm.huffmanCode;

/**
 * 树节点
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/7/20
 */
public class Node implements Comparable<Node>{

    Byte data;
    /**
     * 节点权值
     */
    int weight;
    /**
     * 指向左子结点
     */
    Node left;
    /**
     * 指向右子节点
     */
    Node right;

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

    }

    /**
     * 前序遍历,先输出父结点,再遍历左子树和右子树
     */
    public  void preOrder(){

        System.out.println(this);

        if(null != this.left){
            this.left.preOrder();
        }
        if(null != this.right){
            this.right.preOrder();
        }
    }

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

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



}

  • 哈夫曼编码压缩解压文件
package com.data.algorithm.huffmanCode;

import org.apache.commons.lang3.ArrayUtils;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 哈夫曼编码实现文件的压缩
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/7/21
 */
public class HuffmanCode {

    static Map<Byte, String> huffmanCodes = new HashMap();
    static StringBuilder stringBuilder = new StringBuilder();


    public static void main(String[] args) {
        String srcFile = "E:\\code\\123.txt";
        String zipFile = "E:\\code\\123\\123.zip";
        String dstFile = "E:\\code\\123\\123.txt";
        zipFile(srcFile,zipFile);
        System.out.println("压缩成功!");
        unZipFile(zipFile, dstFile);
        System.out.println("解压成功!");
    }


    /**
     * 文件解压
     * @param zipFile
     * @param dstFile
     */
    public static void unZipFile(String zipFile, String dstFile) {
        InputStream is = null;
        ObjectInputStream ois = null;
        FileOutputStream os = null;

        try {
            is = new FileInputStream(zipFile);
            ois = new ObjectInputStream(is);
            byte[] huffmanBytes = (byte[])ois.readObject();
            Map<Byte, String> huffmanCodes = (Map)ois.readObject();
            byte[] bytes = decode(huffmanCodes, huffmanBytes);
            os = new FileOutputStream(dstFile);
            os.write(bytes);
        } catch (Exception var16) {
            System.out.println(var16.getMessage());
        } finally {
            try {
                os.close();
                ois.close();
                is.close();
            } catch (Exception var15) {
                System.out.println(var15.getMessage());
            }

        }

    }

    /**
     * 文件压缩
     * @param srcFile
     * @param dstFile
     */
    public static void zipFile(String srcFile, String dstFile) {
        OutputStream os = null;
        ObjectOutputStream oos = null;
        FileInputStream is = null;

        try {
            is = new FileInputStream(srcFile);
            byte[] b = new byte[is.available()];
            is.read(b);
            byte[] huffmanBytes = huffmanZip(b);
            os = new FileOutputStream(dstFile);
            oos = new ObjectOutputStream(os);
            oos.writeObject(huffmanBytes);
            oos.writeObject(huffmanCodes);
        } catch (Exception var15) {
            System.out.println(var15.getMessage());
        } finally {
            try {
                is.close();
                oos.close();
                os.close();
            } catch (Exception var14) {
                System.out.println(var14.getMessage());
            }

        }

    }


    /**
     * 哈夫曼编码解码
     * @param huffmanCodes
     * @param huffmanBytes
     * @return
     */
    private static byte[] decode(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) {
        StringBuilder stringBuilder = new StringBuilder();

        for(int i = 0; i < huffmanBytes.length; ++i) {
            byte b = huffmanBytes[i];
            boolean flag = i == huffmanBytes.length - 1;
            stringBuilder.append(byteToBitString(!flag, b));
        }

        Map<String, Byte> map = new HashMap();
        for(Map.Entry<Byte,String> entry:huffmanCodes.entrySet()){
            map.put(entry.getValue(),entry.getKey());
        }

        List<Byte> list = new ArrayList();


        for(int i = 0; i < stringBuilder.length(); ) {
            int count = 1;
            boolean flag = true;
            Byte b = null;

            while(flag) {
                String key = stringBuilder.substring(i, i + count);
                b = (Byte)map.get(key);
                if (b == null) {
                    ++count;
                } else {
                    flag = false;
                }
            }

            list.add(b);
            i+=count;
        }

        byte[] b = new byte[list.size()];

        for(int i = 0; i < b.length; ++i) {
            b[i] = (Byte)list.get(i);
        }

        return b;
    }

    /**
     * 将一个byte转成一个二进制字符串
     * @param flag
     * @param b
     * @return
     */
    private static String byteToBitString(boolean flag, byte b) {
        int temp = b;
        if (flag) {
            temp = b | 256;
        }

        String str = Integer.toBinaryString(temp);
        return flag ? str.substring(str.length() - 8) : str;
    }

    /**
     * 哈夫曼压缩
     * @param bytes
     * @return
     */
    private static byte[] huffmanZip(byte[] bytes) {
        List<Node> nodes = getNodes(bytes);
        Node huffmanTreeRoot = createHuffmanTree(nodes);
        Map<Byte, String> huffmanCodes = getCodes(huffmanTreeRoot);
        byte[] huffmanCodeBytes = zip(bytes, huffmanCodes);
        return huffmanCodeBytes;
    }

    /**
     * 压缩
     * @param bytes
     * @param huffmanCodes
     * @return
     */
    private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
        StringBuilder stringBuilder = new StringBuilder();

        for(byte b:bytes){
            stringBuilder.append(huffmanCodes.get(b));
        }



        int len;
        if (stringBuilder.length() % 8 == 0) {
            len = stringBuilder.length() / 8;
        } else {
            len = stringBuilder.length() / 8 + 1;
        }

        byte[] huffmanCodeBytes = new byte[len];
        int index = 0;

        for(int i = 0; i < stringBuilder.length(); i += 8) {
            String strByte;
            if (i + 8 > stringBuilder.length()) {
                strByte = stringBuilder.substring(i);
            } else {
                strByte = stringBuilder.substring(i, i + 8);
            }

            huffmanCodeBytes[index] = (byte)Integer.parseInt(strByte, 2);
            ++index;
        }

        return huffmanCodeBytes;
    }

    /**
     * 获取哈夫曼编码
     * @param root
     * @return
     */
    private static Map<Byte, String> getCodes(Node root) {
        if (root == null) {
            return null;
        } else {
            getCodes(root.left, "0", stringBuilder);
            getCodes(root.right, "1", stringBuilder);
            return huffmanCodes;
        }
    }

    /**
     * 获取哈夫曼编码
     * @param node
     * @param code
     * @param stringBuilder
     */
    private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
        StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
        stringBuilder2.append(code);
        if (node != null) {
            if (node.data == null) {
                getCodes(node.left, "0", stringBuilder2);
                getCodes(node.right, "1", stringBuilder2);
            } else {
                huffmanCodes.put(node.data, stringBuilder2.toString());
            }
        }

    }

    /**
     * 前序遍历
     * @param root
     */
    private static void preOrder(Node root) {
        if (root != null) {
            root.preOrder();
        } else {
            System.out.println("赫夫曼树为空");
        }

    }

    /**
     * 获取list
     * @param bytes
     * @return
     */
    private static List<Node> getNodes(byte[] bytes) {
        ArrayList<Node> nodes = new ArrayList();
        //统计每个byte出现的次数
        Map<Byte, Integer> counts = new HashMap();
        for(byte b:bytes){
            Integer count = (Integer)counts.get(b);
            if (count == null) {
                counts.put(b, 1);
            } else {
                counts.put(b, count + 1);
            }
        }


        for (Map.Entry<Byte,Integer> entry:counts.entrySet()){
            nodes.add(new Node(entry.getKey(),entry.getValue()));
        }

        return nodes;
    }

    /**
     * 创建哈夫曼树
     * @param nodes
     * @return
     */
    private static Node createHuffmanTree(List<Node> nodes) {

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

        return (Node)nodes.get(0);
    }
}

  • 结果:

在这里插入图片描述

5、哈夫曼编码压缩文件注意事项

  • 如果文件本身就是经过压缩处理的。那么使用哈夫曼编码再压缩效率也不会有明显变化。
  • 哈夫曼编码是按字节来处理的,因此可以处理所有文件。
  • 如果一个文件中的内容重复数据不多,压缩效果也不会很明显。
  • 13
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个Java的霍文件压缩代码示例: ```java import java.io.*; import java.util.*; class HuffmanNode implements Comparable<HuffmanNode> { int frequency; char data; HuffmanNode left, right; public int compareTo(HuffmanNode node) { return this.frequency - node.frequency; } } class HuffmanTree { public static void compressFile(String inputFile, String outputFile) { try { // 读取输入文件 FileInputStream fis = new FileInputStream(inputFile); byte[] inputBytes = new byte[(int) fis.available()]; fis.read(inputBytes); fis.close(); // 统计字符频率 Map<Character, Integer> frequencyMap = new HashMap<>(); for (byte b : inputBytes) { char c = (char) b; frequencyMap.put(c, frequencyMap.getOrDefault(c, 0) + 1); } // 构建霍树 PriorityQueue<HuffmanNode> pq = new PriorityQueue<>(); for (Map.Entry<Character, Integer> entry : frequencyMap.entrySet()) { HuffmanNode node = new HuffmanNode(); node.data = entry.getKey(); node.frequency = entry.getValue(); pq.add(node); } while (pq.size() > 1) { HuffmanNode left = pq.poll(); HuffmanNode right = pq.poll(); HuffmanNode newNode = new HuffmanNode(); newNode.frequency = left.frequency + right.frequency; newNode.left = left; newNode.right = right; pq.add(newNode); } HuffmanNode root = pq.poll(); // 构建霍编码表 Map<Character, String> codeMap = new HashMap<>(); buildCodeMap(root, "", codeMap); // 压缩文件 StringBuilder compressedData = new StringBuilder(); for (byte b : inputBytes) { char c = (char) b; compressedData.append(codeMap.get(c)); } // 写入输出文件 FileOutputStream fos = new FileOutputStream(outputFile); fos.write(compressedData.toString().getBytes()); fos.close(); System.out.println("文件压缩成功!"); } catch (IOException e) { e.printStackTrace(); } } private static void buildCodeMap(HuffmanNode node, String code, Map<Character, String> codeMap) { if (node == null) { return; } if (node.left == null && node.right == null) { codeMap.put(node.data, code); } buildCodeMap(node.left, code + "0", codeMap); buildCodeMap(node.right, code + "1", codeMap); } } public class HuffmanTreeTest { public static void main(String[] args) { String inputFile = "text.txt"; String outputFile = "compressed.txt"; HuffmanTree.compressFile(inputFile, outputFile); } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值