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日

相关文章

  • Spring Boot启动过程全面解析(三)

    针对“SpringBoot启动过程全面解析(三)”这篇文章,我将进行以下详细讲解: 1. 文章简介 这篇文章主要讲解Spring Boot应用程序的启动过程。通过分析Spring Boot框架的源代码,介绍了Spring Boot启动时各个关键步骤的实现过程,帮助读者更好地理解Spring Boot框架的运作机制。 2. Spring Boot的静态资源加载…

    Java 2023年5月15日
    00
  • JAVA/JSP学习系列之四(Orion App Server的安装)

    下面是“JAVA/JSP学习系列之四(Orion App Server的安装)”的完整攻略: 介绍 Orion是一个免费的Java应用服务器,它支持J2EE标准,并且提供了许多有用的工具和功能。下面是Orion的一些特点:- 完全兼容J2EE标准;- 支持Servlet、JSP、EJB和JMS;- 提供了一个可用的控制台管理;- 提供了集成的用户身份验证和安…

    Java 2023年6月16日
    00
  • java常见log日志的使用方法解析

    Java常见log日志的使用方法解析 在Java中,使用log日志来记录系统运行时产生的事件和错误信息十分重要。它可以帮助开发者快速定位问题并解决,提高开发效率。本文将介绍Java常见log日志的使用方法,希望对Java开发者有所帮助。 一、Java常见Log日志框架 Java常见的Log日志框架有java.util.logging、log4j、logbac…

    Java 2023年5月26日
    00
  • 如何把本地jar包导入maven并pom添加依赖

    下面是如何把本地jar包导入maven并pom添加依赖的完整攻略: 1. 将本地jar包导入maven仓库 使用本地jar包,我们需要先将其导入maven仓库里面,这样我们才能在pom文件中引用到它。 步骤如下: 打开命令行窗口,进入到本地jar包所在目录 假设本地jar包文件名为example.jar,执行以下命令: shell mvn install:i…

    Java 2023年5月20日
    00
  • eclipse3.2.2 + MyEclipse5.5 + Tomcat5.5.27 配置数据库连接池

    以下是针对”eclipse3.2.2 + MyEclipse5.5 + Tomcat5.5.27 配置数据库连接池”的完整攻略,包括两条示例说明: 1. 配置Tomcat服务器 首先,需要在Eclipse中配置Tomcat服务器,以便将自己的web项目部署到Tomcat中进行测试。步骤如下: 在Eclipse中点击”Window -> Preferen…

    Java 2023年6月16日
    00
  • JQuery标签页效果实例详解

    接下来我将为你详细讲解“JQuery标签页效果实例详解”的完整攻略。 概述 本文将介绍如何使用 jQuery 实现一个标签页效果。标签页是一种常见的网页布局方式,用户可以通过点击标签来切换不同的内容。在本文中,我们将使用 jQuery 和 CSS 实现一个简单的标签页效果。 实现步骤 创建 HTML 结构 首先需要创建一个 HTML 结构,包含多个标签和对应…

    Java 2023年6月15日
    00
  • spring mvc实现文件上传与下载功能

    Spring MVC实现文件上传与下载功能 Spring MVC是一个非常流行的Java Web框架,它提供了很多方便的功能,其中包括文件上传和下载。本文将详细讲解如何使用Spring MVC实现文件上传和下载功能,并提供两个示例来说明如何实现这一过程。 文件上传 文件上传是Web应用程序中常见的功能之一。Spring MVC提供了很多方便的类和注解来处理文…

    Java 2023年5月17日
    00
  • 深入理解hibernate的三种状态

    深入理解Hibernate的三种状态包括: 瞬时状态(transient state) 持久状态(persistent state) 游离状态(detached state) 瞬时状态(transient state) 当一个新的Java对象被创建时,它处于瞬时状态。Hibernate对该对象并没有关注,在Hibernate Session缓存(first …

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