Java- 哈夫曼(Huffman)压缩算法

Posted ZSYL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java- 哈夫曼(Huffman)压缩算法相关的知识,希望对你有一定的参考价值。

Huffman树

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

如:int a[] = {0,1,2,3,4,5,6,7,8}
在这里插入图片描述
我们可以发现以下规律

1:9个数构成的哈夫曼树一共有17个结点,也就是可以n个数可以生产2*n-1个结点
2:数字越大的数离根节点越近,越小的数离根节点越近。

Huffman编码算法

  • 哈夫曼(Huffman)编码算法

它是基于二叉树构建编码压缩结构的,它是数据压缩中经典的一种算法:根据文本字符出现的频率,重新对字符进行编码。

例如字符串为ABBCCC,其中一种可能的编码是A = 00,B = 01,C = 1,那么这个字符串所对应的哈夫曼编码就是000101111

所以我们希望频率越高的词,编码越短,这样最终才能最大化压缩存储文本数据的空间。

算法分析

  1. 统计词频
  2. 构建哈夫曼树
    1. 每一个不同的字符,是一个叶子结点,频数为结点权重
    2. 结点按照权重升序排序
    3. 每次取出最小的两个结点,构建新的结点,为二者的父结点,其权重为两个子节点的权值之和
    4. 将新结点重新加入原序列,升序排序
    5. 重复上述过程,直到序列中剩下一个结点
    6. 即 哈夫曼树的根结点 root

代码实现

Java中提供的优先队列 PriorityQueue<> 可以很好为我们提供方便。具体请参考一下 OJ题目 及实现代码,感受一下Huffman的强大。

题目描述:

题目描述:
相信大家都对哈夫曼编码有所了解,通常利用哈夫曼编码来压缩数据。现在给你一个字符串,你需要统计其中字符出现的次数然后以出现次数为权值构建一棵哈夫曼树。
例如:字符串为ABBCCC,其中一种可能的编码是A = 00,B = 01,C = 1,那么这个字符串所对应的哈夫曼编码就是000101111
你的任务是求出字符串最终形成的哈夫曼编码的长度。

输入: 一行由大写字母组成的字符串,且字符串长度不超过10000,该字符串最少包含一种字符。
输出:字符串的哈夫曼编码长度。

代码展示:

import java.util.*;
 
 // 定义结点类
class HafNode {
    char name;  // 字符
    int w;  // 权值
    HafNode l;  // 左孩子
    HafNode r; // 右孩子
    
    public HafNode(char name, int w) {
        this.name = name;
        this.w = w;
    }
 
}
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
		
        char[] c = sc.nextLine().toCharArray();
 
        Map<Character, Integer> map = new HashMap<>();
 
        // 统计单词频数
        for (char cc : c) {
            map.put(cc, map.getOrDefault(cc, 0) + 1);
        }
 		// 创建优先队列
        PriorityQueue<HafNode> queue = new PriorityQueue<>((o1, o2) -> o1.w - o2.w);
 
        for (Map.Entry<Character, Integer> entry : map.entrySet()) {
            queue.add(new HafNode(entry.getKey(), entry.getValue()));
        }
 		
 		// 队列中只剩最后一个结点,循环结束
        while (queue.size() > 1) {
            HafNode node1 = queue.poll();
            HafNode node2 = queue.poll();
            HafNode node = new HafNode(' ', node1.w + node2.w);
            node.l = node1;
            node.r = node2;
            queue.offer(node);
        }
        // 获取根结点
        HafNode root = queue.poll();
 
        // 遍历整颗树,使用 BFS算法
        Queue<HafNode> q = new LinkedList<>();
        q.add(root);
        int cnt = -1;  // 层数
 
        map.clear();
 
        while (!q.isEmpty()) {
            HafNode node;
            // 层数++
            cnt++;
            int n = q.size();  // 当前层的结点数
            // 遍历每一层
            for (int i = 0; i < n; i++) {
                node = q.poll();
                // 判断当前结点是否为叶子结点
                if (node.l == null && node.r == null) {
                    map.put(node.name, cnt);  // 修改叶子结点的层数,即编码位数
 
                } else {
                    q.offer(node.l);
                    q.offer(node.r);
                }
            }
        }
 
        long res = 0;
        for (char cc : c) {
            res += map.get(cc);
        }
        System.out.println(res);
    }
}

感谢努力
加油!

以上是关于Java- 哈夫曼(Huffman)压缩算法的主要内容,如果未能解决你的问题,请参考以下文章

使用libjpeg进行图片压缩(哈夫曼算法,无损压缩)

Huffman编码实现压缩解压缩

Huffman编码实现压缩解压缩

Huffman编码实现压缩解压缩

算法系列之赫夫曼编码实战一数据压缩数据解压

哈夫曼编码压缩与解压思路分析与Java代码实现