Java数据结构之哈夫曼树概述及实现

Java数据结构之哈夫曼树概述及实现

哈夫曼树概述

哈夫曼树(Huffman Tree),也称为最优树(Optimal Binary Tree),是一种带权路径长度最短的二叉树,也就是最小权重的前缀编码树。其基本思想是采用频率作为节点的权值,将频率较小的节点放在左子树上,频率较大的节点放在右子树上,从而形成一棵权值最小的二叉树。

实现过程

实现哈夫曼树需要以下几个步骤:

步骤一:将权值列表转化为叶子节点

首先需要将权值列表转化为叶子节点,每个叶子节点保存着一个字符和该字符出现的次数。实现代码如下:

public class Node implements Comparable<Node> {
    int weight;
    char c;
    Node left, right;

    public Node(int weight, char c) {
        this.weight = weight;
        this.c = c;
    }

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

步骤二:建立哈夫曼树

其次,需要将叶子节点插入到优先队列(PriorityQueue)中,并且不断地取出两个权值最小的节点,将它们的权值相加,然后生成一个新的节点作为它们的父亲节点,直到队列中只有一个节点为止。实现代码如下:

public static Node buildHuffmanTree(Map<Character, Integer> freqMap) {
    PriorityQueue<Node> queue = new PriorityQueue<>();
    freqMap.forEach((key, value) -> {
        Node node = new Node(value, key);
        queue.offer(node);
    });

    while (queue.size() > 1) {
        Node left = queue.poll();
        Node right = queue.poll();
        Node parent = new Node(left.weight + right.weight, '-');
        parent.left = left;
        parent.right = right;
        queue.offer(parent);
    }

    return queue.poll();
}

步骤三:生成哈夫曼编码

最后,需要根据哈夫曼树生成每个叶子节点的哈夫曼编码。从根节点出发,如果往左子树方向走,则在该节点的编码后添加0;如果往右子树方向走,则在该节点的编码后添加1。直到走到叶子节点时,所走路径的编码即为该叶子节点的哈夫曼编码。实现代码如下:

public static void generateHuffmanCode(Node node, String code, Map<Character, String> codeMap) {
    if (node.left == null && node.right == null) {
        codeMap.put(node.c, code);
    } else {
        generateHuffmanCode(node.left, code + "0", codeMap);
        generateHuffmanCode(node.right, code + "1", codeMap);
    }
}

示例说明

示例一

假设输入为一个字符串 "ABACA",需求为对该字符串进行哈夫曼编码。则可使用以下代码进行演示:

public static void main(String[] args) {
    String input = "ABACA";

    // 计算每个字符的频率
    Map<Character, Integer> freqMap = new HashMap<>();
    for (char c : input.toCharArray()) {
        freqMap.put(c, freqMap.getOrDefault(c, 0) + 1);
    }

    // 构建哈夫曼树
    Node root = buildHuffmanTree(freqMap);

    // 生成哈夫曼编码
    Map<Character, String> codeMap = new HashMap<>();
    generateHuffmanCode(root, "", codeMap);

    // 输出哈夫曼编码
    for (Map.Entry<Character, String> entry : codeMap.entrySet()) {
        System.out.println(entry.getKey() + " : " + entry.getValue());
    }
}

结果输出如下:

C : 00
B : 01
A : 1

示例二

假设输入为一个文件 "input.txt",需求为对该文件内容进行哈夫曼编码。则可使用以下代码进行演示:

public static void main(String[] args) throws IOException {
    String inputFileName = "input.txt";
    String outputFileName = "output.txt";

    // 读取输入文件中的内容,并计算每个字符的频率
    StringBuilder inputBuilder = new StringBuilder();
    Map<Character, Integer> freqMap = new HashMap<>();
    try (BufferedReader reader = new BufferedReader(new FileReader(inputFileName))) {
        String line;
        while ((line = reader.readLine()) != null) {
            for (char c : line.toCharArray()) {
                inputBuilder.append(c);
                freqMap.put(c, freqMap.getOrDefault(c, 0) + 1);
            }
            inputBuilder.append("\n");
        }
    }

    String input = inputBuilder.toString();

    // 构建哈夫曼树
    Node root = buildHuffmanTree(freqMap);

    // 生成哈夫曼编码
    Map<Character, String> codeMap = new HashMap<>();
    generateHuffmanCode(root, "", codeMap);

    // 输出哈夫曼编码到输出文件中,并将原文件中的内容按哈夫曼编码进行压缩
    try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputFileName))) {
        for (Map.Entry<Character, String> entry : codeMap.entrySet()) {
            writer.write(entry.getKey() + " : " + entry.getValue() + "\n");
        }
        writer.write("\n");

        for (char c : input.toCharArray()) {
            if (codeMap.containsKey(c)) {
                writer.write(codeMap.get(c));
            }
        }
    }
}

其中输入文件 "input.txt" 的内容为:

hello world
how are you

输出文件 "output.txt" 的内容为:

  : 100
d : 1010
e : 110
h : 00
l : 111
o : 01
r : 1011
w : 1001
y : 1000

001111110011111100101101011101000111000001100110111111010100101011110100010101000000100011100011011
1110110011101000111111010010010001100100110010000101101101

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java数据结构之哈夫曼树概述及实现 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • Java 数据结构与算法系列精讲之贪心算法

    Java 数据结构与算法系列精讲之贪心算法 什么是贪心算法? 在计算机科学中,贪心算法是一种通过选择局部最优解来实现全局最优解的优化算法。贪心算法在解决某些最优化问题时非常有效,贪心算法能够达到接近最优解,有时甚至能够达到最优解。 贪心算法解题步骤: 建立算法模型 找出最优解的子结构 设计贪心选择策略 实现贪心选择策略为一个最优解 证明贪心算法的正确性 贪心…

    数据结构 2023年5月17日
    00
  • C++实现LeetCode(211.添加和查找单词-数据结构设计)

    首先,我们需要先了解一下题目的要求和限制,以及具体的解题思路。 题目描述 设计一个支持添加、删除、查找单词的数据结构。添加和删除单词的操作需要支持普通词和通配符’.’。查找单词只支持普通词,不支持通配符’.’。所有单词都是非空的。 解题思路 这道题可以使用前缀树(Trie树)来实现。 首先,我们需要定义一个单词类,它包含两个字段:单词字符串和单词长度。然后,…

    数据结构 2023年5月17日
    00
  • Java数据结构之线段树的原理与实现

    Java数据结构之线段树的原理与实现 什么是线段树 线段树是一种基于分治思想的数据结构,它可以用来解决各种区间查询问题,例如区间求和、最大值、最小值等等。在算法竞赛和数据结构课程中,线段树被广泛应用,是一种非常实用的数据结构。 线段树的基本原理 线段树是一种二叉树,它的每个节点包含一个区间,叶子节点表示区间中的单个元素,非叶子节点表示区间的合并。 线段树的建…

    数据结构 2023年5月17日
    00
  • C++数据结构与算法之双缓存队列实现方法详解

    C++数据结构与算法之双缓存队列实现方法详解 引言 在实际开发中,双缓存队列是一个非常常见的数据结构,主要用来解决多线程情况下的数据同步问题。本篇文章将详细介绍如何使用C++语言实现双缓存队列。 双缓存队列简介 双缓存队列是一种常用的同步数据结构,它并非一个标准库中的容器,通常需要手动实现。双缓存队列维护着两个缓存区,一个当前使用的缓存区,一个需要被更新的缓…

    数据结构 2023年5月17日
    00
  • 带你了解Java数据结构和算法之前缀,中缀和后缀表达式

    带你了解Java数据结构和算法之前缀、中缀和后缀表达式 1. 前缀表达式(Prefix Expression) 前缀表达式是指运算符位于操作数之前的表达式,也被称为波兰式。前缀表达式的优点在于,每个运算符的优先级都可以通过括号来明确,不需要考虑运算符的优先级。同时,整个表达式的意义也可以很清晰地传达。 举个例子,下面是一个前缀表达式: + * 4 5 6 /…

    数据结构 2023年5月17日
    00
  • Java数据结构之加权无向图的设计实现

    Java数据结构之加权无向图的设计实现 前言 在计算机科学中,图(Graph)作为一种基本数据结构,被广泛应用于各种领域,如网络流、图像处理、计算机视觉等。本文将介绍加权无向图(Weighted Undirected Graph)的设计实现,涉及图的存储、添加边、获取特定节点的相邻节点、计算最短路径等。 设计实现 存储结构 加权无向图可以用一个邻接表数组存储…

    数据结构 2023年5月17日
    00
  • 数据结构 双机调度问题的实例详解

    数据结构:双机调度问题的实例详解 本文主要讲解数据结构中双机调度问题的实例详解,涉及到相关的算法和代码实现。双机调度问题是指如何安排多个任务在两台机器上执行,使得两台机器的工作时间尽可能相等,从而达到最优的调度效果。 1. 问题分析 假设有 $n$ 个任务,每个任务的执行时间分别为 $t_1, t_2, …, t_n$,需要按照某种调度方案分配给两台机器…

    数据结构 2023年5月17日
    00
  • JavaScript的Set数据结构详解

    JavaScript中的Set数据结构详解 什么是Set? Set 是一种 Javascript 内置的数据结构。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,可以通过new关键字来创建 Set 数据结构。 let mySet = new Set(); Set的基本用法 Set实例对象有以下常用方法: add(value):…

    数据结构 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部