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数据结构之单链表详解

    下面是单链表攻略的详细讲解。 什么是单链表? 单链表是一种线性数据结构,它由一系列结点组成,每个结点包含数据域和指针域。数据域用于存储数据,指针域用于指向下一个结点。单链表的优点是插入和删除操作的时间复杂度为O(1),缺点是随机访问的时间复杂度为O(n)。 单链表的基本操作 单链表的基本操作包括插入操作、删除操作、查找操作和遍历操作。下面将分别介绍这些操作。…

    数据结构 2023年5月17日
    00
  • C语言全面梳理结构体知识点

    C语言全面梳理结构体知识点 什么是结构体? 结构体是一种自定义的数据类型,它可以包含多个不同类型的成员变量,并且这些成员变量可以通过一个变量名来访问。结构体的定义需要使用关键字struct,并且需要指定结构体的类型名和成员变量。例如: struct Person { char name[20]; int age; float height; }; 以上代码就…

    数据结构 2023年5月17日
    00
  • 用C语言实现单链表的各种操作(一)

    “用C语言实现单链表的各种操作(一)”详细介绍了如何通过C语言来实现单链表的常见操作。下面,我会结合该文章的内容,对其进行完整攻略的介绍。 文章的主要内容包括:单链表的定义、单链表的初始化、判断单链表是否为空、获取单链表中元素个数、在链表开头插入元素、在链表末尾插入元素、在链表中间插入元素、删除链表中指定元素、在链表中查找指定元素、链表的反转以及链表的销毁。…

    数据结构 2023年5月17日
    00
  • Java数据结构学习之栈和队列

    Java数据结构学习之栈和队列 什么是栈 栈(stack)是一种线性数据结构,它只能在一端进行插入和删除操作,这一端被称作栈顶(top)。栈的特点是先进后出(FILO,First-In-Last-Out),即最后进入的元素最先被删除。 栈的实现方式 栈可以使用数组或链表来实现。使用数组实现的栈称作顺序栈,使用链表实现的栈称作链式栈。以下是顺序栈的 Java …

    数据结构 2023年5月17日
    00
  • C、C++线性表基本操作的详细介绍

    我来详细讲解“C、C++线性表基本操作的详细介绍”。 一、线性表的定义 线性表是一种数据结构,它是由n个数据元素组成的有限序列,记为(a1,a2,…,an),其中a1是线性表的第一个元素,an是线性表的最后一个元素。除第一个元素之外,每一个元素有且仅有一个直接前驱元素,除了最后一个元素之外,每一个元素有且仅有一个直接后继元素。 线性表可以理解为一个一维数…

    数据结构 2023年5月17日
    00
  • [Week 19]每日一题(C++,数学,并查集,动态规划)

    目录 [Daimayuan] T1 倒数第n个字符串(C++,进制) 输入格式 输出格式 样例输入 样例输出 解题思路 [Daimayuan] T2 排队(C++,并查集) 输入格式 输出格式 样例输入1 样例输出1 样例输入2 样例输出2 样例输入3 样例输出3 数据规模 解题思路 [Daimayuan] T3 素数之欢(C++,BFS) 数据规模 输入格…

    算法与数据结构 2023年5月4日
    00
  • C#数据结构与算法揭秘二 线性结构

    C#数据结构与算法揭秘二 线性结构 线性结构是指数据元素之间一对一的关系,即数据元素之间存在一个前驱和一个后继。一般有两种基本形式:线性表和栈、队列。 线性表 线性表是由同类型数据元素构成有序序列的线性结构,常被用于实现基于数组的数据结构,如向量、矩阵等。 线性表可以分为顺序表和链表两种。 顺序表(Sequence List):是把线性表的元素按照顺序存储在…

    数据结构 2023年5月17日
    00
  • C语言链表案例学习之通讯录的实现

    让我详细讲解一下“C语言链表案例学习之通讯录的实现”的完整攻略。 1. 案例简介 本案例的目的是通过实现一个简单的通讯录程序,来学习C语言链表的原理和操作。程序主要功能涵盖通讯录添加、删除、修改以及查询。 2. 程序架构 程序的整体结构如下所示: 头文件声明 结构体定义 函数声明 主函数 函数实现 其中,头文件声明包含stdio.h、stdlib.h以及st…

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