Java利用哈夫曼编码实现字符串压缩

Java利用哈夫曼编码实现字符串压缩

介绍

哈夫曼编码是一种可变长度编码,它在通信和数据压缩领域得到广泛的应用。在哈夫曼编码中,出现频率高的字符或词语将被分配短的编码,出现频率低的则分配长的编码,这样可以有效地减少数据的传输量和存储空间。

本攻略将介绍如何使用Java实现字符串的压缩和解压缩,其中包括使用哈夫曼编码来实现压缩。

步骤

以下是压缩和解压缩的完整步骤:

压缩

  1. 统计字符串中每个字符出现的次数,生成字符频率表。
  2. 将字符频率表构建成哈夫曼树。
  3. 生成哈夫曼编码表,将每个字符对应的哈夫曼编码保存在表中。
  4. 将原始字符串按照哈夫曼编码表进行编码,生成压缩后的字节数组。
  5. 将编码后的字节数组写入文件。

示例1:假设还原字符串为“abcbdcb”, 其中a出现1次,b出现2次,c出现2次,d出现1次,则字符频率表如下:

字符 频率
a 1
b 2
c 2
d 1

生成的哈夫曼树如下:

       6
     /    \
    b(2)   .(4)
          /   \
        2     c(2)
        / \
      a(1) d(1)

生成的哈夫曼编码表如下:

字符 哈夫曼编码
a 001
b 01
c 00
d 11

原始字符串使用生成的哈夫曼编码编码后,得到的压缩字节数组为:

0001101100110101001000001

解压缩

  1. 从压缩文件中读取字节数组。
  2. 根据字节数组生成二进制字符串。
  3. 根据哈夫曼编码表将二进制字符串转换为原始字符串,完成解压缩。
  4. 将原始字符串写入文件。

示例2:假设压缩后得到的字节数组为:0001101100110101001000001。使用上述示例1中的哈夫曼编码表,可以将字节数组转换为二进制字符串:

0001101100110101001000001
  ^^^   ^^ ^  ^  ^^   ^
  c  b  d  c  a  b   d

然后再根据哈夫曼编码表将二进制字符串转换为原始字符串,“0001101100110101001000001”经过解码后得到的原始字符串是“abcbdcb”。

代码示例

下面的代码展示了如何使用Java实现压缩和解压缩,包括哈夫曼编码的实现。它使用了Java的BitSet类来实现高效的比特位操作,以便在压缩和解压缩时能够快速地访问和操作比特位。

import java.util.Map;
import java.util.HashMap;
import java.util.PriorityQueue;

public class HuffmanCoding {
    // 定义一个哈夫曼树节点的类
    private static class Node implements Comparable<Node> {
        final char ch;  // 字符
        final int freq; // 频率
        final Node left, right; // 左子节点和右子节点

        Node(char ch, int freq, Node left, Node right) {
            this.ch = ch;
            this.freq = freq;
            this.left = left;
            this.right = right;
        }

        // 实现优先队列所需要的compareTo方法
        public int compareTo(Node that) {
            return this.freq - that.freq;
        }
    }

    // 构建哈夫曼树
    private static Node buildTree(Map<Character, Integer> freq) {
        PriorityQueue<Node> pq = new PriorityQueue<>();
        for (Map.Entry<Character, Integer> entry : freq.entrySet()) {
            pq.offer(new Node(entry.getKey(), entry.getValue(), null, null));
        }
        while (pq.size() > 1) {
            Node left = pq.poll();
            Node right = pq.poll();
            pq.offer(new Node('\0', left.freq + right.freq, left, right));
        }
        return pq.poll();
    }

    // 根据哈夫曼树生成哈夫曼编码表
    private static Map<Character, String> buildTable(Node root) {
        Map<Character, String> table = new HashMap<>();
        buildTableHelper(root, "", table);
        return table;
    }

    private static void buildTableHelper(Node x, String s, Map<Character, String> table) {
        if (x.left == null && x.right == null) {
            table.put(x.ch, s);
            return;
        }
        buildTableHelper(x.left, s + '0', table);
        buildTableHelper(x.right, s + '1', table);
    }

    // 哈夫曼编码的压缩
    public static byte[] compress(String text) {
        Map<Character, Integer> freq = new HashMap<>();
        for (char ch : text.toCharArray()) {
            freq.put(ch, freq.getOrDefault(ch, 0) + 1);
        }

        Node root = buildTree(freq);
        Map<Character, String> table = buildTable(root);

        BitSet bits = new BitSet();
        int size = 0;
        for (char ch : text.toCharArray()) {
            String code = table.get(ch);
            for (int i = 0; i < code.length(); i++) {
                if (code.charAt(i) == '1') {
                    bits.set(size);
                }
                size++;
            }
        }

        byte[] bytes = new byte[(size + 7) / 8];
        for (int i = 0; i < size; i++) {
            if (bits.get(i)) {
                bytes[i / 8] |= (1 << (7 - i % 8));
            }
        }
        return bytes;
    }

    // 哈夫曼编码的解压缩
    public static String decompress(byte[] bytes) {
        BitSet bits = BitSet.valueOf(bytes);

        StringBuilder sb = new StringBuilder();
        Node x = root;
        for (int i = 0; i < bits.length(); i++) {
            x = bits.get(i) ? x.right : x.left;
            if (x.left == null && x.right == null) {
                sb.append(x.ch);
                x = root;
            }
        }
        return sb.toString();
    }
}

压缩示例代码

使用上述HuffmanCoding类实现字符串压缩的示例代码:

String text = "abcbdcb";
byte[] compressed = HuffmanCoding.compress(text);
// 将压缩后的字节数组写入文件

解压缩示例代码

使用上述HuffmanCoding类实现字符串解压缩的示例代码:

byte[] compressed = // 从文件中读取压缩后的字节数组
String original = HuffmanCoding.decompress(compressed);
// 将解压缩后的原始字符串写入文件

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java利用哈夫曼编码实现字符串压缩 - Python技术站

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

相关文章

  • Springboot2.0配置JPA多数据源连接两个mysql数据库方式

    下面是关于Springboot2.0配置JPA多数据源连接两个mysql数据库的完整攻略: 1. 配置application.properties文件 在application.properties文件中配置两个数据源的连接信息,如下所示: #第一个数据源 spring.datasource.test1.jdbc-url=jdbc:mysql://local…

    Java 2023年5月20日
    00
  • SpringMVC简单整合Angular2的示例

    简介 SpringMVC和Angular2都是非常优秀的Web开发框架,将它们整合起来可以有效提高Web应用的开发效率和质量。本示例主要介绍了如何在SpringMVC项目中简单地整合Angular2,实现一个简单的用户注册和登录表单。 环境准备 在开始整合之前,需要准备好以下环境: Java JDK 8 Maven SpringMVC 4.3.x Angul…

    Java 2023年6月16日
    00
  • java == 引发的线上异常详解

    让我来详细讲解一下“java == 引发的线上异常详解”。 概述 在Java开发中,我们通常会使用“==”来比较两个对象是否相等。但是,如果使用不当,就可能会引发线上异常。本文将会详细探讨在Java中使用“==”可能会遇到的问题,以及如何避免这些问题。 引发异常的问题 基本类型与包装类比较 在Java中,基本类型和其对应的包装类是不同的类型,它们互相之间并不…

    Java 2023年5月27日
    00
  • SpringBoot导出Word文档的三种方式

    SpringBoot导出Word文档的三种方式 一、导出方案 1、直接在Java代码里创建Word文档,设置格式样式等,然后导出。(略) 需要的见:https://blog.csdn.net/qq_42682745/article/details/120867432 2、富文本转换后的HTML下载为Word文档。相当于把HTML转为Word导出 3、使用模板…

    Java 2023年5月4日
    00
  • 使用SpringBoot内置web服务器

    使用Spring Boot内置web服务器来快速搭建Web应用是非常方便的。下面是使用Spring Boot内置web服务器的完整攻略,包括配置步骤和示例说明。 配置步骤 创建一个Spring Boot应用。在pom.xml中添加以下依赖: <dependency> <groupId>org.springframework.boot&…

    Java 2023年6月2日
    00
  • 浅谈springmvc 通过异常增强返回给客户端统一格式

    以下是关于“浅谈SpringMVC通过异常增强返回给客户端统一格式”的完整攻略,其中包含两个示例。 浅谈SpringMVC通过异常增强返回给客户端统一格式 在SpringMVC中,我们可以通过异常增强的方式来统一处理异常,并将异常信息以统一的格式返回给客户端。在本文中,我们将讲解如何通过异常增强的方式来实现这一功能。 异常增强实现原理 SpringMVC通过…

    Java 2023年5月17日
    00
  • 浅析JS获取url中的参数实例代码

    首先,获取URL中的参数是Web开发经常需要的功能。JavaScript提供了多种方式可以获取URL参数,本文将介绍其中两种最常见的方式:URLSearchParams和正则表达式。 使用URLSearchParams URLSearchParams是一个原生对象,主要用来操作URL查询参数。使用URLSearchParams获取URL参数非常方便。 我们可…

    Java 2023年6月15日
    00
  • JDBC的扩展知识点总结

    下面我会详细讲解“JDBC的扩展知识点总结”的完整攻略。 JDBC的扩展知识点总结 什么是JDBC Java数据库连接(Java Database Connectivity,简称JDBC)是Java语言中用于执行SQL语句的一组API。通俗地讲,JDBC就是Java语言连接数据库的一个标准规范。使用JDBC,可以使Java程序与任何支持SQL的关系型数据库进…

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