赫夫曼编码—数据压缩算法

赫夫曼编码是一种无前缀的压缩算法,原理是将字符按权重生成赫夫曼树,赫夫曼树特点是WPL最小,字符位于叶子节点,保证权重大(出现次数多)的字符离根节点进,对字符做二进制编码,具体压缩和解压过程:
压缩:
1.原始字符串-->各个字符出现次数(权重),字符权重List;
2.字符权重List-->赫夫曼树;
3.赫夫曼树-->赫夫曼编码表;
4.赫夫曼编码-->压缩编码;
解压:
压缩编码-->匹配赫夫曼编码,得到原始数据;

该压缩算法,核心是利用赫夫曼树的2个特性:

1. WPL值最小;

2. 编码无前缀;

注:本文demo里面的二进制编码使用了string表示,是为了简化过程和方便可视化,实际环境要使用byte[]类型存储;

demo中测试用例:

压缩前原始数据:

i like like like java do you like a java

赫夫曼编码压缩后数据:

1010100010111111110010001011111111001000101111111100100101001101110001110000011011101000111100101000101111111100110001001010011011100

赫夫曼树:

 

赫夫曼编码:

字符权重编码
d111000
u111001
y111010
j20010
o20011
v211011
l4000
e41110
k41111
a5100
i5101
空格901

 

节点类 Node.java

/**
 * @Author: ltx
 * @Description: BST二叉树节点
 */
public class Node implements Comparable<Node> {
    public Character value;//节点值
    public Integer weight;//节点权重
    public Node left;//左子节点
    public Node right;//右子节点

    public Node(Character value, Integer weight) {
        this.value = value;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return String.valueOf(value) + ":" + String.valueOf(weight);
    }

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

赫夫曼算法类 Huffman.java

/**
 * @Author: ltx
 * @Description: 赫夫曼编码
 */
public class Huffman {
    Map<Character, String> codeMap = new HashMap<>();

    /**
     * 中序遍历(前序或后序也行)
     * 得到huffman编码
     *
     * @param node 节点
     * @param sb   StringBuilder
     */
    public void getCodeByMidOrder(Node node, StringBuilder sb) {
        if (node.left != null) {
            //往左走+'0'
            sb.append("0");
            getCodeByMidOrder(node.left, sb);
        }
        //叶子节点
        if (node.left == null && node.right == null) {
//            System.out.print(node + ":" + sb + ", ");
            codeMap.put(node.value, sb.toString());
        }
        if (node.right != null) {
            //往右走+'1'
            sb.append("1");
            getCodeByMidOrder(node.right, sb);
        }
        //回溯向上走减少一个字符
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }
    }

    public Node getHuffmanTree(List<Node> nodes) {
        Collections.sort(nodes);
//        System.out.println(nodes);
        while (nodes.size() > 1) {
            //左叶子-数组最小值
            Node leafLeft = nodes.remove(0);
            //右叶子-数组次小值
            Node leafRight = nodes.remove(0);
            //父节点=数组最小值+数组次小值
            Node parentNode = new Node(null, leafLeft.weight + leafRight.weight);
            parentNode.left = leafLeft;
            parentNode.right = leafRight;
            //新的节点添加到list里面
            nodes.add(parentNode);
            Collections.sort(nodes);
        }
        return nodes.get(0);
    }

    /**
     * 获取各个字符权重(字符个数)
     *
     * @param str
     * @return
     */
    public List<Node> getCharWight(String str) {
        Map<Character, Integer> charNumMap = new HashMap<>();
        char[] chs = str.toCharArray();
        for (char c : chs) {
            //数量
            Integer count = charNumMap.get(c);
            if (count == null) {
                charNumMap.put(c, 1);
            } else {
                charNumMap.put(c, count + 1);
            }
        }
//        System.out.println(charNumMap);
        List<Node> nodes = new ArrayList<>();
        //创建节点List
        for (char c : charNumMap.keySet()) {
            nodes.add(new Node(c, charNumMap.get(c)));
        }
        return nodes;
    }

    /**
     * 编码
     * 根据赫夫曼编码表,对原始数据做转换
     *
     * @param str 原始字符串
     * @return
     */
    public String encoding(String str) {
        StringBuilder sb = new StringBuilder();
        char[] chs = str.toCharArray();
        for (char c : chs) {
            sb.append(codeMap.get(c));
        }
        return sb.toString();
    }

    /**
     * 解码
     *
     * @return
     */
    public String decode(String encodStr) {
        //翻转codeMap
        Map<String, Character> encodeMap = new HashMap<>();
        for (char c : codeMap.keySet()) {
            encodeMap.put(codeMap.get(c), c);
        }
        char[] chs = encodStr.toCharArray();
        int j = 0;
        StringBuilder res = new StringBuilder();
        while (j < encodStr.length() - 1) {
            StringBuilder sb = new StringBuilder();
            Character cr;
            //匹配出字符串
            while ((cr = encodeMap.get(sb.toString())) == null) {
                sb.append(chs[j]);
                j++;
            }
            res.append(cr);
        }
        return res.toString();
    }


    public static void main(String[] args) {
        Huffman huffman = new Huffman();
        System.out.println("原始数据:");
        //需要压缩的串
        String str = "i like like like java do you like a java";
        System.out.println(str);
        //1.获得字符权重List
        List<Node> nodes = huffman.getCharWight(str);
        //[ :9, a:5, d:1, e:4, u:1, v:2, i:5, y:1, j:2, k:4, l:4, o:2]
        System.out.println("节点权重列表:");
        System.out.println(nodes);
        //2.构造赫夫曼树
        Node root = huffman.getHuffmanTree(nodes);
        //3.获得赫夫曼编码表
        huffman.getCodeByMidOrder(root, new StringBuilder(""));
        System.out.println("赫夫曼编码表:");
        //得到赫夫曼编码表
        //{ =01, a=100, d=11000, u=11001, e=1110, v=11011, i=101, y=11010, j=0010, k=1111, l=000, o=0011}
        System.out.println(huffman.codeMap);
        //4.压缩编码
        String encodStr = huffman.encoding(str);
        System.out.println("压缩后数据:");
        //1010100010111111110010001011111111001000101111111100100101001101110001110000011011101000111100101000101111111100110001001010011011100
        System.out.println(encodStr);
        //5.解压
        String res = huffman.decode(encodStr);
        System.out.println("解压后数据:");
        //打印最终结果
        //i like like like java do you like a java
        System.out.println(res);
    }
}

测试用例: 

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小绿豆

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值