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日

相关文章

  • MT6589平台通话录音时播放提示音给对方功能的具体实现

    要实现“MT6589平台通话录音时播放提示音给对方功能”,需要在两个方面进行修改: 修改系统代码,使得当调用通话录音时,系统能够在录音开始时往话筒播放提示音; 修改通话录音应用程序的源代码,使得当开始录音时,能够调用系统接口往话筒播放提示音。 下面将具体介绍实现这一功能的步骤和示例: 步骤一:修改系统代码 打开系统源代码,找到通话录音相关的文件,例如Audi…

    Java 2023年5月23日
    00
  • bootstrap weebox 支持ajax的模态弹出框

    Bootstrap是一套UI框架,其中Weebox是一个基于Bootstrap的模态弹出框插件,支持AJAX加载内容。本攻略将详细介绍如何使用Bootstrap Weebox插件实现AJAX加载内容的模态弹出框。 准备工作 引入Bootstrap和jQuery库。 <link rel="stylesheet" href="…

    Java 2023年6月16日
    00
  • SpringBoot之webflux全面解析

    Spring Boot WebFlux是Spring Boot的一个重要特性,它提供了一种基于响应式编程模型的Web开发方式。以下是Spring Boot WebFlux的完整攻略: 添加WebFlux依赖 在Spring Boot中,我们可以使用Maven或Gradle来添加WebFlux依赖。以下是一个Maven的示例: <dependency&g…

    Java 2023年5月15日
    00
  • MySQL特定表全量、增量数据同步到消息队列-解决方案

    下面我会分四个部分详细讲解MySQL特定表全量、增量数据同步到消息队列的解决方案。 1. 数据库准备 首先,我们需要有一个MySQL数据库实例,并在其中创建需要同步的特定表。为了方便演示,这里创建一个test数据库和一张users表: CREATE DATABASE test; USE test; CREATE TABLE `users` ( `id` in…

    Java 2023年5月20日
    00
  • js获取客户端网卡的IP地址、MAC地址

    获取客户端网卡的IP地址和MAC地址涉及到两个不同的技术点,分别是使用JavaScript获取客户端IP地址和使用Java Applet获取网卡的MAC地址。 使用JavaScript获取客户端IP地址 在JavaScript中,可以通过window.RTCPeerConnection对象来获取客户端的IP地址,具体过程如下: // 定义一个全局变量,用来存…

    Java 2023年6月15日
    00
  • Java Spring 循环依赖解析

    下面是“Java Spring 循环依赖解析”的完整攻略。 什么是循环依赖? 在 Spring 容器中,如果两个或多个 Bean 相互依赖,且这种互相依赖形成了环路,就会出现循环依赖。 例如,BeanA依赖BeanB,而BeanB又依赖BeanA,则会形成一个循环依赖。 如何解决循环依赖? Spring 解决循环依赖的方式称为循环依赖解析。当 Spring …

    Java 2023年5月20日
    00
  • Springboot jdbctemplate整合实现步骤解析

    下面是“Springboot jdbctemplate整合实现步骤解析”的完整攻略,包含了整合步骤、示例代码和讲解。 SpringBoot JdbcTemplate整合实现步骤解析 1. 添加依赖 首先需要在SpringBoot工程中添加对JdbcTemplate的依赖,可以在pom.xml中添加如下依赖: <dependency> <gr…

    Java 2023年6月16日
    00
  • Java面试题冲刺第二十一天–JVM

    Java面试题冲刺第二十一天–JVM 一、了解JVM 1. JVM的概念 JVM(Java Virtual Machine)即Java虚拟机,是Java语言的运行环境,负责将Java字节码文件转换为机器指令执行。 2. JVM的内部结构 JVM的内部结构分为三个部分:类加载器,运行时数据区,执行引擎。 2.1 类加载器 用来加载类文件,包括如下几种类型: …

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