算法系列之数据结构-Huffman树

简介: Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。

_20250306_190203.png

在数据压缩领域,Huffman编码是一种经典的无损压缩算法,而Huffman树则是实现这种编码的关键数据结构。它以其高效性和简洁性被广泛应用于各种场景,从文件压缩到通信协议,都离不开Huffman树的身影。本文将深入探讨Huffman树的原理、构建过程以及其Java如何实现Huffman树。

Huffman树的构建步骤

Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。

以下是Huffman树的构建步骤:

  1. 统计字符频率:统计待压缩数据中每个字符出现的频率。

  2. 构建优先队列:将每个字符及其频率作为一个节点,放入优先队列(最小堆)中。

  3. 合并节点:从优先队列中取出两个频率最小的节点,合并成一个新的节点,新节点的频率为这两个节点的频率之和,然后将新节点放回优先队列。

  4. 重复合并:重复步骤 3,直到优先队列中只剩一个节点,这个节点就是 Huffman 树的根节点。

  5. 生成编码:从根节点开始,向左走为 0,向右走为 1,遍历整棵树,生成每个字符的 Huffman 编码。

比如有字符串及频率集合:{a: 5,b: 9,c: 12,d: 13,e: 36},下图为构建过程:

_20250306_180610.png

Huffman 解码

Huffman生成编码之后我们可以用Huffman的编码代替字符串,达到数据压缩的操作。反过来,我们要解码的时候也可以由Huffman树便捷的完成。解码过程:从头开始扫描二进制编码串,从Huffman树的根节点开始,根据比特位不断地进入下一层节点,当遇到0时进入左子节点,遇到1时进入右子节点;到达叶子节点后输出对应的字符,然后重复次步骤,直到二进制串完毕,则可以得到解码之后的字符串。

Java实现Huffman树

以下是由java实现的Huffman树的构建、编码及解码。

/**
 * Huffman树节点实体类
 */
@Data
public class HuffmanTreeNode implements Comparable<HuffmanTreeNode>{
   
    //字符
    private char data;
    //权重
    private int weight;
    //左子节点
    private HuffmanTreeNode left;
    //右子节点
    private HuffmanTreeNode right;

    //需要对权重进行排序,重写compareTo接口
    @Override
    public int compareTo(HuffmanTreeNode o) {
   
        if(o.getWeight() > this.getWeight()){
   
            return 1;
        }
        if(o.getWeight() < this.getWeight()){
   
            return -1;
        }
        return 0;
    }
    public HuffmanTreeNode(char data, int weight) {
   
        this.data = data;
        this.weight = weight;
    }
}



/**
 * Huffman树
 */
public class HuffmanTreeExample {
   

    /**
     * 构建Huffman树
     * 使用优先队列来构建Huffman树
     */
    public static HuffmanTreeNode build(Map<Character, Integer> charMap) {
   
        // 将字符频率表中的每个字符及其频率作为节点加入优先队列
        PriorityQueue<HuffmanTreeNode> queue = new PriorityQueue<>();
        for(Map.Entry<Character, Integer> entry : charMap.entrySet()){
   
            queue.add(new HuffmanTreeNode(entry.getKey(), entry.getValue()));
        }
        // 循环构建Huffman树,当队列中只剩一个节点时,Huffman树构建完成,此节点为Huffman的根节点
        while(queue.size() > 1){
   
            // 取出两个频率最小的节点
            HuffmanTreeNode left = queue.poll();
            HuffmanTreeNode right = queue.poll();
            // 将两个节点合并为一个父节点,父节点的频率为两个子节点的频率之和
            HuffmanTreeNode parent = new HuffmanTreeNode('\0', left.getWeight() + right.getWeight());
            parent.setLeft(left);
            parent.setRight(right);
            // 将父节点加入队列
            queue.add(parent);
        }

        //构建完成,返回根节点
        return queue.poll();
    }

    /**
     * 生成Huffman编码
     * @param root,节点
     * @param code,当前节点编码
     * @param huffmancode 编码map
     */
    public static void generateHuffmanCodes(HuffmanTreeNode root, String code,Map<Character,String> huffmancode) {
   
        //根节点为空则返回
        if(root == null){
   
            return;
        }
        if(root.getLeft() == null && root.getRight() == null){
   
            //叶子节点
            huffmancode.put(root.getData(),code);
        }else{
   
            //非叶子节点,递归遍历左右子树
            generateHuffmanCodes(root.getLeft(),code + "0",huffmancode);
            generateHuffmanCodes(root.getRight(),code + "1",huffmancode);
        }
    }

    /**
     * Huffman编码
     */
    public static String encode(String str, Map<Character, String> huffmancode) {
   
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
   
            sb.append(huffmancode.get(str.charAt(i)));
        }
        return sb.toString();
    }

    /**
     * Huffman解码
     */
    public static String decode(String str, HuffmanTreeNode root) {
   
        StringBuilder sb = new StringBuilder();
        HuffmanTreeNode node = root;
        // 遍历字符串,根据字符对应的编码,找到对应的叶子节点,输出字符
        for (int i = 0; i < str.length(); i++) {
   
            if (str.charAt(i) == '0') {
   
                node = node.getLeft();
            } else {
   
                node = node.getRight();
            }
            //叶子节点,输出字符,重置node为根节点
            if (node.getLeft() == null && node.getRight() == null) {
   
                sb.append(node.getData());
                node=root;
            }
        }
        return sb.toString();
    }



    public static void main(String[] args) {
   
        String text = "hello world";
        //统计字符出现频率
        Map<Character, Integer> charMap = new HashMap<>();
        for (char c : text.toCharArray()) {
   
            charMap.put(c, charMap.getOrDefault(c, 0) + 1);
        }
        //构建Huffman树
        HuffmanTreeNode root = build(charMap);
        //获取Huffman编码
        Map<Character, String> huffmancode = new HashMap<>();
        generateHuffmanCodes(root, "", huffmancode);
        System.out.println("Huffman编码:"+huffmancode.toString());
        //对字符串进行Huffman编码
        String encodeStr = encode(text, huffmancode);
        System.out.println("Huffman编码后的字符串:"+encodeStr);
        //对字符串进行Huffman解码
        String decodeStr = decode(encodeStr, root);
        System.out.println("Huffman解码后的字符串:"+decodeStr);


    }
}

Huffman 树的应用

Huffman 树主要用于数据压缩领域,常见的应用场景包括:

  • 文件压缩:如 ZIP、GZIP 等压缩工具中使用了 Huffman 编码。

  • 图像压缩:如 JPEG 图像格式中使用了 Huffman 编码。

  • 网络传输:在数据传输过程中,使用 Huffman 编码可以减少数据量,提高传输效率。

总结

Huffman 树是一种高效的数据压缩算法,通过构建带权路径长度最短的二叉树来实现数据压缩。Java 中可以通过优先队列和递归的方式实现 Huffman 树的构建和编码生成。Huffman 树在文件压缩、图像压缩和网络传输等领域有广泛的应用。

目录
相关文章
|
4月前
|
存储 机器学习/深度学习 监控
网络管理监控软件的 C# 区间树性能阈值查询算法
针对网络管理监控软件的高效区间查询需求,本文提出基于区间树的优化方案。传统线性遍历效率低,10万条数据查询超800ms,难以满足实时性要求。区间树以平衡二叉搜索树结构,结合节点最大值剪枝策略,将查询复杂度从O(N)降至O(logN+K),显著提升性能。通过C#实现,支持按指标类型分组建树、增量插入与多维度联合查询,在10万记录下查询耗时仅约2.8ms,内存占用降低35%。测试表明,该方案有效解决高负载场景下的响应延迟问题,助力管理员快速定位异常设备,提升运维效率与系统稳定性。
275 4
|
7月前
|
存储 监控 安全
企业上网监控系统中红黑树数据结构的 Python 算法实现与应用研究
企业上网监控系统需高效处理海量数据,传统数据结构存在性能瓶颈。红黑树通过自平衡机制,确保查找、插入、删除操作的时间复杂度稳定在 O(log n),适用于网络记录存储、设备信息维护及安全事件排序等场景。本文分析红黑树的理论基础、应用场景及 Python 实现,并探讨其在企业监控系统中的实践价值,提升系统性能与稳定性。
304 1
|
9月前
|
存储 机器学习/深度学习 算法
KMP、Trie树 、AC自动机‌ ,三大算法实现 优雅 过滤 netty 敏感词
KMP、Trie树 、AC自动机‌ ,三大算法实现 优雅 过滤 netty 敏感词
KMP、Trie树 、AC自动机‌ ,三大算法实现 优雅 过滤 netty  敏感词
|
7月前
|
监控 算法 安全
基于 C# 基数树算法的网络屏幕监控敏感词检测技术研究
随着数字化办公和网络交互迅猛发展,网络屏幕监控成为信息安全的关键。基数树(Trie Tree)凭借高效的字符串处理能力,在敏感词检测中表现出色。结合C#语言,可构建高时效、高准确率的敏感词识别模块,提升网络安全防护能力。
193 2
|
9月前
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
234 17
|
7月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
205 0
|
9月前
|
存储 监控 算法
局域网上网记录监控的 C# 基数树算法高效检索方案研究
在企业网络管理与信息安全领域,局域网上网记录监控是维护网络安全、规范网络行为的关键举措。随着企业网络数据量呈指数级增长,如何高效存储和检索上网记录数据成为亟待解决的核心问题。基数树(Trie 树)作为一种独特的数据结构,凭借其在字符串处理方面的卓越性能,为局域网上网记录监控提供了创新的解决方案。本文将深入剖析基数树算法的原理,并通过 C# 语言实现的代码示例,阐述其在局域网上网记录监控场景中的具体应用。
207 7
|
11月前
|
人工智能 算法 语音技术
Video-T1:视频生成实时手术刀!清华腾讯「帧树算法」终结闪烁抖动
清华大学与腾讯联合推出的Video-T1技术,通过测试时扩展(TTS)和Tree-of-Frames方法,显著提升视频生成的连贯性与文本匹配度,为影视制作、游戏开发等领域带来突破性解决方案。
382 4
Video-T1:视频生成实时手术刀!清华腾讯「帧树算法」终结闪烁抖动
|
8月前
|
机器学习/深度学习 算法 搜索推荐
决策树算法如何读懂你的购物心理?一文看懂背后的科学
"你为什么总能收到刚好符合需求的商品推荐?你有没有好奇过,为什么刚浏览过的商品就出现了折扣通知?
246 0
|
11月前
|
存储 自然语言处理 数据库
【数据结构进阶】AVL树深度剖析 + 实现(附源码)
在深入探讨了AVL树的原理和实现后,我们不难发现,这种数据结构不仅优雅地解决了传统二叉搜索树可能面临的性能退化问题,还通过其独特的平衡机制,确保了在任何情况下都能提供稳定且高效的查找、插入和删除操作。
828 19