Java 最优二叉树的哈夫曼算法的简单实现

Java 最优二叉树的哈夫曼算法的简单实现

一、哈夫曼编码算法简介

哈夫曼编码(Huffman coding)是一种无损压缩编码,广泛用于数据的压缩和传输。哈夫曼编码利用字符出现的频率进行编码,出现频率高的字符对应的编码短,反之出现频率低的字符对应的编码长,从而达到减少数据存储空间和传输带宽的目的。

哈夫曼编码的核心思想是构造哈夫曼树,将出现频率高的字符作为叶子节点,出现频率低的字符作为离叶子节点较近的节点,以此构造一个最优二叉树来实现编码。

二、哈夫曼编码算法过程

下面是哈夫曼编码算法的详细步骤:

1. 统计频率

遍历待编码的字符串,统计每个字符出现的频率,并存储到频率表中。

2. 构造哈夫曼树

通过频率表构造哈夫曼树:将出现频率低的字符作为左子树,出现频率高的字符作为右子树,从而构造出最优二叉树。

3. 生成编码表

以根节点为起点,向下递归遍历整个哈夫曼树,对于每个叶子节点记录它们的哈夫曼编码,生成编码表。

4. 编码数据

利用生成的编码表对数据进行编码,将原字符串中的字符依次转换成对应的哈夫曼编码串。

5. 解码数据

利用生成的哈夫曼树对哈夫曼编码串进行解码,将哈夫曼编码串转换成原字符串中的字符。

三、Java 实现哈夫曼编码算法

下面是使用 Java 语言实现哈夫曼编码算法的简单示例:


import java.util.*;
public class HuffmanCoding {

    /**
    * 统计字符出现的频率
    *
    * @param data 待编码的字符串
    * @return Map<Character, Integer> 每个字符出现的频率
    */
    public static Map<Character, Integer> getFrequency(String data) {
        Map<Character, Integer> frequency = new HashMap<Character, Integer>();
        for (int i = 0; i < data.length(); i++) {
            char c = data.charAt(i);
            if (frequency.containsKey(c)) {
                frequency.put(c, frequency.get(c) + 1);
            } else {
                frequency.put(c, 1);
            }
        }
        return frequency;
    }

    /**
    * 构造哈夫曼树
    *
    * @param frequency 每个字符出现的频率
    * @return Node 哈夫曼树的根节点
    */
    public static Node buildHuffmanTree(Map<Character, Integer> frequency) {
        PriorityQueue<Node> queue = new PriorityQueue<Node>();

        // 初始化节点队列
        for (Character c : frequency.keySet()) {
            queue.offer(new Node(c, frequency.get(c), null, null));
        }

        // 构建哈夫曼树
        while (queue.size() > 1) {
            Node leftNode = queue.poll();
            Node rightNode = queue.poll();
            Node parent = new Node(null, leftNode.frequency + rightNode.frequency, leftNode, rightNode);
            queue.offer(parent);
        }

        // 返回根节点
        return queue.poll();
    }

    /**
    * 遍历哈夫曼树,生成编码表
    *
    * @param root 哈夫曼树的根节点
    * @return Map<Character, String> 每个字符对应的哈夫曼编码
    */
    public static Map<Character, String> getCodingMap(Node root) {
        Map<Character, String> codingMap = new HashMap<Character, String>();
        getCode(root, "", codingMap);
        return codingMap;
    }

    private static void getCode(Node node, String code, Map<Character, String> codingMap) {
        if (node.isLeaf()) {
            codingMap.put(node.data, code);
            return;
        }
        getCode(node.left, code + "0", codingMap);
        getCode(node.right, code + "1", codingMap);
    }

    /**
    * 对数据进行编码
    *
    * @param data 待编码的字符串
    * @param codingMap 每个字符对应的哈夫曼编码
    * @return String 哈夫曼编码串
    */
    public static String encode(String data, Map<Character, String> codingMap) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < data.length(); i++) {
            sb.append(codingMap.get(data.charAt(i)));
        }
        return sb.toString();
    }

    /**
    * 对哈夫曼编码串进行解码
    *
    * @param code 哈夫曼编码串
    * @param root 哈夫曼树的根节点
    * @return String 解码后的字符串
    */
    public static String decode(String code, Node root) {
        StringBuilder sb = new StringBuilder();
        Node node = root;
        for (int i = 0; i < code.length(); i++) {
            char c = code.charAt(i);
            node = (c == '0') ? node.left : node.right;
            if (node.isLeaf()) {
                sb.append(node.data);
                node = root;
            }
        }
        return sb.toString();
    }

    /**
    * 节点类
    */
    static class Node implements Comparable<Node> {
        Character data;             // 节点值(叶子节点才有值)
        Integer frequency;          // 节点出现频率
        Node left;                  // 左子节点
        Node right;                 // 右子节点

        public Node(Character data, Integer frequency, Node left, Node right) {
            this.data = data;
            this.frequency = frequency;
            this.left = left;
            this.right = right;
        }

        /**
        * 判断节点是否为叶子节点
        */
        public boolean isLeaf() {
            return left == null && right == null;
        }

        /**
        * 优先队列中的比较方法
        */
        @Override
        public int compareTo(Node node) {
            return frequency - node.frequency;
        }
    }

    public static void main(String[] args) {
        String data = "hello world, i am a Java program.";
        Map<Character, Integer> frequency = getFrequency(data);
        Node root = buildHuffmanTree(frequency);
        Map<Character, String> codingMap = getCodingMap(root);
        String code = encode(data, codingMap);
        String text = decode(code, root);
        System.out.println("原数据:" + data);
        System.out.println("编码后:" + code);
        System.out.println("解码后:" + text);
    }
}

四、示例

  1. 编码示例:

待编码字符串:hello world, i am a Java program.

统计字符出现频率:{r=2, a=4, ,=6, h=1, d=1, e=1, J=1, g=1, l=3, o=4, i=1, m=2, n=1, p=1, W=1, .=1}

构造哈夫曼树:

               *
             /
            21
           /  \
         /      \
        8        *
       / \      / \
      /   \    /   \
     3     * 17    4
          / \
         /   \
        7     10
       / \   /  \
      2   5 7    3

生成编码表:

{ =111, a=00, .=1000, d=1111, e=1001, g=1011, h=11011, i=10100, J=110100, l=010, m=1100, n=10101, o=01, p=110101, r=011, W=110001}

编码结果:

`110111010100101100111110 …

  1. 解码示例:

待解码字符串: 110111010100101100111110 … (同上)

哈夫曼树(同上)

解码结果:

hello world, i am a Java program.

通过上述示例,我们可以清晰地了解哈夫曼编码算法的实现过程和原理,并且也能够准确地对数据进行编码和解码。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java 最优二叉树的哈夫曼算法的简单实现 - Python技术站

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

相关文章

  • Java点餐小程序之黑心商人

    Java点餐小程序之黑心商人完整攻略 简介 这是一款基于Java实现的点餐小程序,允许用户查看、点餐、结算等操作,并包含了“黑心商人”功能,允许商家设置并收取“加急费”、“删单费”等不合理费用。作为一名程序员,我们应该注重代码的质量,不容忍这种黑心商业行为,本文将详细讲解该小程序的实现过程,并提供几条防止黑心商户的方法。 整体思路 该小程序主要分为前台用户界…

    Java 2023年5月23日
    00
  • java实现附件预览(openoffice+swftools+flexpaper)实例

    可以分为以下几个步骤来实现Java实现附件预览: 安装OpenOffice OpenOffice是一款免费、开源的办公软件套装,包含字处理、电子表格、演示文稿、数据库等基础应用。我们需要利用OpenOffice来将文档转换为PDF,代码如下: private static void officeToPDF(String sourceFilePath, Str…

    Java 2023年5月20日
    00
  • Java Apache Commons报错“ZipException”的原因与解决方法

    “ZipException”是Java的Apache Commons类库中的一个异常,通常由以下原因之一引起: 压缩文件错误:如果压缩文件存在错误,则可能会出现此异常。例如,可能会使用错误的压缩文件格式或压缩文件已损坏。 文件路径错误:如果文件路径错误,则可能会出现此异常。例如,可能会使用错误的文件路径或文件不存在。 以下是两个实例: 例1 如果压缩文件存在…

    Java 2023年5月5日
    00
  • 2022 最新 IntelliJ IDEA 详细配置步骤演示(推荐)

    2022 最新 IntelliJ IDEA 详细配置步骤演示(推荐) IntelliJ IDEA 是一款经典的集成开发环境,支持多种编程语言,包括 Java、Python、Kotlin、Ruby 等等。在使用 IntelliJ IDEA 进行开发之前,我们必须进行一些配置,以便更好地使用这个开发工具。本文将详细介绍 IntelliJ IDEA 的配置步骤。如…

    Java 2023年5月20日
    00
  • SpringBoot 配合 SpringSecurity 实现自动登录功能的代码

    下面我就来详细讲解一下 “SpringBoot 配合 SpringSecurity 实现自动登录功能的代码”的完整攻略。 什么是自动登录功能 自动登录(Remember Me)是指用户可以选择保存登录状态,保留一定时间不失效。这样用户可以在再次打开网站时,不需要重新输入用户名密码,而是直接使用之前的登录信息登录进去。 操作步骤 1. 导入相关依赖 在 pom…

    Java 2023年5月20日
    00
  • MySql多表查询 事务及DCL

    MySQL是一个开源的关系型数据库管理系统,用于管理大量数据,支持多种查询操作,而多表查询、事务及DCL(数据控制语言)是使用MySQL时必须掌握的重要知识点。 多表查询 在MySQL中,多表查询是指同时使用多个表中的数据进行查询操作。多表查询通常使用JOIN关键字实现,常见的JOIN类型有INNER JOIN、LEFT JOIN、RIGHT JOIN和FU…

    Java 2023年6月1日
    00
  • JAVA中读取文件(二进制,字符)内容的几种方法总结

    下面是题目要求的详细攻略: JAVA中读取文件(二进制,字符)内容的几种方法总结 一、读取二进制文件内容 1. FileInputStream 使用 FileInputStream 可以读取二进制文件的内容。 public static byte[] readContentByFileInputStream(String filePath) throws I…

    Java 2023年5月20日
    00
  • Java编程异常处理最佳实践【推荐】

    Java编程异常处理最佳实践【推荐】 异常是Java编程的重要组成部分。良好的异常处理可以更好地保证程序的健壮性、可读性和可维护性。下面是Java编程异常处理的最佳实践: 1. 异常类型的选择 Java中提供了一些异常类型,例如Checked Exception、UnChecked Exception和Error。在编写代码时,需要根据具体的情况选择合适的异…

    Java 2023年5月27日
    00
合作推广
合作推广
分享本页
返回顶部