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日

相关文章

  • java如何把逗号分隔的String字符串转int集合

    要把逗号分隔的字符串转换为整数集合,可以使用Java中的split()方法将字符串分割,然后使用Integer.parseInt()方法将分割后的字符串转换为整数,最后将整数添加到集合中。以下是完整的攻略: 步骤一:将逗号分隔的字符串转为字符串数组 使用String类的split()方法可以将逗号分隔的字符串转化为字符串数组。 String str = &q…

    Java 2023年5月20日
    00
  • java环境中的JDK、JVM、JRE详细介绍

    JDK、JVM、JRE介绍 在学习Java编程语言时,经常会听到JDK、JVM、JRE这几个概念。那么,这些概念的具体含义是什么呢? JDK(Java Development Kit):Java开发工具包。JDK是Java开发的核心组件,包含了Java编译器、Java运行环境、Java类库等一系列组件。 JRE(Java Runtime Environmen…

    Java 2023年5月24日
    00
  • springboot学习之构建简单项目搭建步骤详解

    Spring Boot 学习之构建简单项目搭建步骤详解 介绍 Spring Boot 是一个快速、跨平台、微服务框架,受到了很多 Java 开发者的喜欢。构建一个简单的 Spring Boot 项目并不困难,本篇文章将详细讲解如何搭建一个简单的 Spring Boot 项目。 步骤 以下是构建简单项目所需的步骤: 步骤 1:创建一个新的 Spring Boo…

    Java 2023年5月15日
    00
  • Java语言实现对MySql数据库中数据的增删改查操作的代码

    下面是Java语言实现对MySql数据库中数据的增删改查操作的完整攻略。这里使用JDBC API来操作数据库。 步骤 步骤一:导入JDBC API和JDBC驱动包 在项目中引入JDBC API 和 MySQL Connector/J驱动包,这里以Maven为例,在pom.xml中添加如下依赖: <!– JDBC API –> <depe…

    Java 2023年5月19日
    00
  • Java项目工程代码深度刨析总结

    Java项目工程代码深度刨析总结攻略 1. 熟悉项目工程整体结构 首先,我们需要熟悉Java项目工程的整体结构,这包括项目的目录结构、源码目录结构、所使用的框架、依赖管理工具等。通常情况下,一个Java项目的目录结构应该包括src、lib、test等三个大文件夹以及其他配置文件。 2. 逐个分析源代码 接下来,我们需要逐个分析源代码,深入了解每个类、方法的功…

    Java 2023年5月23日
    00
  • java nio基础使用示例

    下面是“Java NIO基础使用示例”的完整攻略。 什么是Java NIO Java NIO(New IO)是Java SE 1.4中引入的一个新IO API,它支持高速度的I/O,非阻塞式I/O、可扩展的I/O操作和更好的内存管理等特性。相对于传统的Java I/O API,Java NIO更为灵活、高效,因此在高负载的网络应用中得到了广泛的应用。 Jav…

    Java 2023年5月26日
    00
  • Java获得一个数组的指定长度排列组合算法示例

    下面详细讲解一下Java获得一个数组的指定长度排列组合算法示例的完整攻略。 算法说明 在程序设计中,经常会遇到需要从给定的元素集合中去选取一些元素,这些元素能组成的各种可能长度的排列和组合集合。这时候,排列和组合问题就变得特别重要。在Java中,提供了一些工具类帮助我们解决这些问题。 排列和组合的定义 排列问题中,给定n个元素,从中选取k个元素进行排列,若n…

    Java 2023年5月26日
    00
  • 详解五种方式让你在java中读取properties文件内容不再是难题

    让我来详细讲解“详解五种方式让你在Java中读取properties文件内容不再是难题”的完整攻略。 一、背景知识 properties是Java中常用的一种配置文件格式,通常用来存储键-值对。在Java中,可以通过Properties类来读取和写入properties文件。 二、五种方式 1. 使用Properties类的load方法 可以使用Proper…

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