java实现哈夫曼压缩与解压缩的方法

Java实现哈夫曼压缩与解压缩的方法

哈夫曼编码是一种有效的无损压缩算法,常用于压缩文本文件等数据。本文将详细介绍如何使用Java实现哈夫曼压缩与解压缩的方法。

哈夫曼压缩

1. 构建哈夫曼树

首先需要构建一个哈夫曼树,该树的每个叶子节点都代表一个字符,并且每个叶子节点的编码都是唯一的。构建哈夫曼树的过程如下:

  • 统计给定文本中每个字符出现的频率。
  • 将字符频率作为权值构建一棵小根堆(优先队列)。
  • 从堆中取出两个权值最小的节点,并将它们合并为一棵树,并将根节点的权值设置为两个子节点的权值之和。
  • 将新生成的树作为一个节点重新加入堆中。
  • 重复以上步骤,直到堆里只剩下一个节点,即树的根节点。

2. 获取字符编码

在构建哈夫曼树之后,需要根据该树计算出每个叶子节点的编码。对于树中的每个节点,左子节点的编码是当前节点的编码加上字符0,右子节点的编码是当前节点的编码加上字符1。可以使用深度优先遍历的方式,递归地计算出所有叶子节点的编码。

3. 压缩数据

获取字符编码之后,可以根据编码将数据进行压缩。将每个字符替换成它的编码,最终得到一个由0和1组成的二进制字符串。由于Java中没有二进制字符串类型,可以使用StringBuilder类来实现。

以下是具体的Java代码示例:

import java.util.*;

public class HuffmanCompressor {
    private static class Node implements Comparable<Node> {
        public int value;
        public char ch;
        public Node left;
        public Node right;

        public Node(int value, char ch) {
            this.value = value;
            this.ch = ch;
        }

        public Node(int value) {
            this.value = value;
        }

        public boolean isLeaf() {
            return left == null && right == null;
        }

        @Override
        public int compareTo(Node other) {
            return Integer.compare(value, other.value);
        }
    }

    public static String compress(String data) {
        int[] freqTable = new int[256];
        for (int i = 0; i < data.length(); i++) {
            freqTable[data.charAt(i)]++;
        }

        PriorityQueue<Node> pqueue = new PriorityQueue<>();
        for (int i = 0; i < freqTable.length; i++) {
            if (freqTable[i] > 0) {
                pqueue.offer(new Node(freqTable[i], (char) i));
            }
        }

        while(pqueue.size() > 1) {
            Node left = pqueue.poll();
            Node right = pqueue.poll();

            Node parent = new Node(left.value + right.value);
            parent.left = left;
            parent.right = right;

            pqueue.offer(parent);
        }

        Node root = pqueue.poll();

        Map<Character, String> codeTable = new HashMap<>();
        getCodeTable(root, "", codeTable);

        StringBuilder encodedData = new StringBuilder();
        for (int i = 0; i < data.length(); i++) {
            encodedData.append(codeTable.get(data.charAt(i)));
        }

        return encodedData.toString();
    }

    private static void getCodeTable(Node node, String code, Map<Character, String> codeTable) {
        if (node == null) {
            return;
        }

        if (node.isLeaf()) {
            codeTable.put(node.ch, code);
        }

        getCodeTable(node.left, code + "0", codeTable);
        getCodeTable(node.right, code + "1", codeTable);
    }
}

哈夫曼解压缩

1. 构建哈夫曼树

哈夫曼解压缩需要用到与压缩相同的哈夫曼树,因此需要先构建哈夫曼树。步骤与压缩时相同,将编码和字符对应关系调换即可。

2. 解码数据

将二进制编码的数据根据哈夫曼编码进行解码。从树的根节点开始,依次读取每一个二进制位,如果是0,则移动到当前节点的左子节点,如果是1,则移动到当前节点的右子节点,直到遇到叶子节点。当遇到叶子节点时,将该节点的字符存储下来,并从根节点重新开始处理下一个编码。

以下是具体的Java代码示例:

public class HuffmanDecompressor {
    private static class Node {
        public char ch;
        public Node left;
        public Node right;

        public Node(char ch) {
            this.ch = ch;
        }

        public boolean isLeaf() {
            return left == null && right == null;
        }
    }

    public static String decompress(String data, Node root) {
        StringBuilder decodedData = new StringBuilder();

        Node curr = root;
        for (int i = 0; i < data.length(); i++) {
            if (data.charAt(i) == '0') {
                curr = curr.left;
            } else if (data.charAt(i) == '1') {
                curr = curr.right;
            }

            if (curr.isLeaf()) {
                decodedData.append(curr.ch);
                curr = root;
            }
        }

        return decodedData.toString();
    }
}

示例

示例1:压缩文件

下面的代码示例演示如何使用上面的HuffmanCompressor类将文件(已存在的test.txt文件)压缩并写入到新文件test.gz中,并解压缩到解压缩文件test_decoded.txt。

import java.io.*;
import java.nio.file.Files;

public class Main {
    public static void main(String[] args) throws IOException {
        String filePath = "test.txt";
        String compressedFilePath = "test.gz";
        String decompressedFilePath = "test_decoded.txt";

        // 读取源文件
        byte[] dataBytes = Files.readAllBytes(new File(filePath).toPath());
        String data = new String(dataBytes);

        // 压缩
        String compressedData = HuffmanCompressor.compress(data);

        // 将压缩后的数据写入到文件
        FileOutputStream fos = new FileOutputStream(compressedFilePath);
        byte[] compressedBytes = compressedData.getBytes("UTF-8");
        fos.write(compressedBytes);
        fos.close();

        // 从压缩文件中读取数据
        String compressedDataFromFile = new String(Files.readAllBytes(new File(compressedFilePath).toPath()), "UTF-8");

        // 解压缩
        String decompressedData = HuffmanDecompressor.decompress(compressedDataFromFile, getHuffmanTree(data));

        // 将解压缩后的数据写入到文件
        FileOutputStream fos2 = new FileOutputStream(decompressedFilePath);
        byte[] decompressedBytes = decompressedData.getBytes("UTF-8");
        fos2.write(decompressedBytes);
        fos2.close();
    }

    private static HuffmanDecompressor.Node getHuffmanTree(String data) {
        int[] freqTable = new int[256];
        for (int i = 0; i < data.length(); i++) {
            freqTable[data.charAt(i)]++;
        }

        PriorityQueue<HuffmanCompressor.Node> pqueue = new PriorityQueue<>();
        for (int i = 0; i < freqTable.length; i++) {
            if (freqTable[i] > 0) {
                pqueue.offer(new HuffmanCompressor.Node(freqTable[i], (char) i));
            }
        }

        while(pqueue.size() > 1) {
            HuffmanCompressor.Node left = pqueue.poll();
            HuffmanCompressor.Node right = pqueue.poll();

            HuffmanCompressor.Node parent = new HuffmanCompressor.Node(left.value + right.value);
            parent.left = left;
            parent.right = right;

            pqueue.offer(parent);
        }

        return pqueue.poll();
    }
}

示例2:压缩字符串

以下代码演示如何使用上面的HuffmanCompressor类将字符串压缩并解压缩。

public class Main {
    public static void main(String[] args) {
        String data = "Hello, World!";
        String compressedData = HuffmanCompressor.compress(data);
        String decompressedData = HuffmanDecompressor.decompress(compressedData, getHuffmanTree(data));

        System.out.println("原始数据:" + data);
        System.out.println("压缩后的数据:" + compressedData);
        System.out.println("解压缩后的数据:" + decompressedData);
    }

    private static HuffmanDecompressor.Node getHuffmanTree(String data) {
        int[] freqTable = new int[256];
        for (int i = 0; i < data.length(); i++) {
            freqTable[data.charAt(i)]++;
        }

        PriorityQueue<HuffmanCompressor.Node> pqueue = new PriorityQueue<>();
        for (int i = 0; i < freqTable.length; i++) {
            if (freqTable[i] > 0) {
                pqueue.offer(new HuffmanCompressor.Node(freqTable[i], (char) i));
            }
        }

        while(pqueue.size() > 1) {
            HuffmanCompressor.Node left = pqueue.poll();
            HuffmanCompressor.Node right = pqueue.poll();

            HuffmanCompressor.Node parent = new HuffmanCompressor.Node(left.value + right.value);
            parent.left = left;
            parent.right = right;

            pqueue.offer(parent);
        }

        return pqueue.poll();
    }
}

希望这些示例能够帮助您更好地了解如何使用Java实现哈夫曼压缩和解压缩。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java实现哈夫曼压缩与解压缩的方法 - Python技术站

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

相关文章

  • web.xml SpringBoot打包可执行Jar运行SpringMVC加载流程

    web.xml SpringBoot打包可执行Jar运行SpringMVC加载流程 在 SpringBoot 中,我们可以使用可执行 Jar 包来运行我们的应用程序。本文将详细讲解如何使用 web.xml 文件来配置 SpringMVC,并将其打包为可执行 Jar 包。 1. 创建 SpringBoot 项目 首先,我们需要创建一个 SpringBoot 项…

    Java 2023年5月18日
    00
  • 使用JDBC工具类实现简单的登录管理系统

    使用JDBC工具类实现简单的登录管理系统需要以下步骤: 准备工作 在项目中引入JDBC依赖,如使用Maven引入jdbc依赖: <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> &l…

    Java 2023年6月16日
    00
  • 基于Java中的数值和集合详解

    基于Java中的数值和集合详解 本文将介绍 Java 中的数值类型和集合类的基本知识,同时提供几个示例,帮助读者更好地理解这些概念。 数值类型 Java 中的基本数据类型包括整型(int 和 long)、浮点型(float 和 double)、字符型(char)和布尔型(boolean)。这些类型在计算机编程中非常常见,因此应当掌握。 整型 整型分为 int…

    Java 2023年5月26日
    00
  • Java日期时间及日期相互转换实现代码

    下面是“Java日期时间及日期相互转换实现代码”的完整攻略: 1. Java日期时间基础 Java 日期时间类是 Java API 内置的类,主要包括以下两个部分: 日期类:Date 类是 JDK 1.0 中的类,主要用于表示日期和时间。 日期格式类:DateFormat 是格式化日期时间的抽象类,它可以将 Date 类型的时间格式化为指定格式的字符串,也可…

    Java 2023年5月20日
    00
  • spring整合kaptcha验证码的实现

    以下是详细讲解“Spring整合Kaptcha验证码的实现”的完整攻略,包括相关代码示例和说明: 1. 概述 Kaptcha是一个开源的验证码生成工具,可以生成常见的验证码图片。Spring框架是目前广泛使用的Java Web开发框架。将Spring与Kaptcha整合可以快速实现验证码功能,提高网站的安全性。 2. 引入Kaptcha 首先需要引入Kapt…

    Java 2023年6月15日
    00
  • Spring Boot整合Web项目常用功能详解

    下面我给你详细讲解SpringBoot整合Web项目常用功能的完整攻略: 一、概述 SpringBoot是一种可以简化Spring应用程序的创建和开发过程的框架。在Web应用程序中,常见的功能包括:前端页面开发、路由、数据接收和处理、数据持久化等。SpringBoot在这些方面均提供了相应的支持和优化,能够让Web应用的开发更加高效和方便。 二、常用功能 1…

    Java 2023年5月15日
    00
  • Tomcat源码导入idea的方法

    以下是详细的Tomcat源码导入idea的方法: 步骤一:下载Tomcat源码并解压 首先,你需要在Apache Tomcat下载页面[https://tomcat.apache.org/download-80.cgi]上下载该版本的Tomcat源码,然后将其解压到任意目录。 步骤二:安装Java和IDEA 在继续之前,你需要先安装Java和IDEA。确保你…

    Java 2023年6月15日
    00
  • SpringMVC Restful风格与中文乱码问题解决方案介绍

    SpringMVC Restful风格与中文乱码问题解决方案介绍 在 Spring MVC 中,我们可以使用 Restful 风格来设计 Web 应用程序。Restful 风格是一种基于 HTTP 协议的 Web 应用程序设计风格,它可以帮助我们更好地设计和实现 Web 应用程序。但是,在使用 Restful 风格时,我们可能会遇到中文乱码问题。本文将详细讲…

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