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++抽象数据类型介绍

    C++抽象数据类型介绍 什么是抽象数据类型? 抽象数据类型(Abstract Data Type,ADT),是数据类型的一个数学模型。它实现了数据类型的抽象过程,将数据与操作分离,使得操作具有独立性,而数据只作为函数参数和返回值存在。 举个例子,ADT可以定义一个栈(Stack),栈的实现需要以下操作: 初始化栈 压入数据 弹出数据 获取栈顶数据 检查栈是否…

    数据结构 2023年5月17日
    00
  • Java数据结构常见几大排序梳理

    Java数据结构常见几大排序梳理 在Java中,数据排序是非常常见的操作。下面我们详细讲解一下Java数据结构常见几大排序的梳理。 常见几大排序 Java数据结构中常见几种排序算法包括: 冒泡排序(Bubble Sort) 快速排序(Quick Sort) 插入排序(Insertion Sort) 选择排序(Selection Sort) 希尔排序(Shel…

    数据结构 2023年5月17日
    00
  • C语言超详细讲解数据结构中的线性表

    C语言超详细讲解数据结构中的线性表完整攻略 线性表的概念和基本操作 线性表是指由同类型的数据元素构成的有限序列。即每个数据元素只有一个前驱和一个后继。线性表通常用于表示一维数组、列表、队列等数据结构。 线性表的基本操作包括: 初始化操作:创建一个空的线性表。 插入操作:在线性表中插入一个元素。 删除操作:删除线性表中的一个元素。 查找操作:查找线性表中是否存…

    数据结构 2023年5月17日
    00
  • C语言数据结构之复杂链表的拷贝

    C语言数据结构之复杂链表的拷贝 什么是复杂链表 在了解如何拷贝复杂链表之前,首先需要知道什么是复杂链表。复杂链表是由多个节点组成的链表,每个节点除了包含普通链表节点的值和指向下一个节点的指针外,还包含一个指向链表中的任意一个节点的指针。因此,每个节点有两个指针:一个指向下一个节点,一个指向任意一个节点。 复杂链表示意图如下: +—+ +—+ +—…

    数据结构 2023年5月17日
    00
  • C++ 数据结构之kmp算法中的求Next()函数的算法

    C++ 数据结构之kmp算法中的求Next()函数的算法 什么是KMP算法和Next()函数 KMP算法(Knuth-Morris-Pratt算法)是一种字符串匹配算法,能够解决的问题是,在一个文本串S中查找一个模式串P是否出现并且返回第一次出现的位置。而Next()函数则是在KMP算法中使用的一个关键的子函数,用于计算模式串P中每个前缀的最长相同真前缀和后…

    数据结构 2023年5月17日
    00
  • 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解

    解析从源码分析常见的基于Array的数据结构动态扩容机制的详解 什么是动态扩容机制 动态扩容机制是指,当一个数据结构达到其容量限制时,自动增加容量大小以继续存储新的数据。在动态扩容时,需要考虑到时间和空间的平衡,因为扩容需要分配新的内存空间,在处理大量数据时,需要尽可能减少空间浪费和分配内存的时间消耗。 基于Array的数据结构 Array是一种连续存储的数…

    数据结构 2023年5月17日
    00
  • C++LeetCode数据结构基础详解

    C++LeetCode数据结构基础详解攻略 什么是LeetCode? LeetCode是一个专门为程序员提供的算法题平台。平台上汇集了各种算法、数据结构和编程题,用户可以在平台上挑战各种难度的算法用来提高自己的编程能力和算法素养。 如何学习LeetCode? 学习LeetCode的关键是掌握数据结构和算法。下面介绍如何结合具体的C++代码来学习LeetCod…

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