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日

相关文章

  • JSP页面间传值问题实例简析

    下面是对JSP页面间传值问题实例简析的完整攻略: 1. 问题分析 在使用JSP进行web页面开发的过程中,经常需要使用多个JSP页面来完成相应的业务功能,这时候我们就需要在不同的JSP页面之间传递参数或对象。 JSP页面间传值的情景: 当我们在JSP页面中调用另外一个JSP页面或Servlet时,可能需要将当前页面中的某些数据传递给其它页面或Servlet进…

    Java 2023年6月15日
    00
  • SpringBoot概述及在idea中创建方式

    SpringBoot概述 Spring Boot是一个开源的Java框架,它摆脱了传统Spring框架的繁琐配置,建立在Spring Framework的基础之上。Spring Boot提供了一种快速简便的方式来搭建Java应用程序,并且默认设置对各种Spring组件、外部组件、配置管理等进行了很好的支持。 Spring Boot使用“约定大于配置”的方式来…

    Java 2023年5月15日
    00
  • 基于Java SSM实现在线点餐系统

    下面就详细讲解基于Java SSM实现在线点餐系统的完整攻略。 1. 系统设计 1.1 系统架构 在线点餐系统的系统架构主要包括四部分:前端展示、后台管理、数据库系统和服务器部署。其中,前端展示部分采用HTML、CSS和JavaScript等技术实现,后台管理部分采用Java SSM框架构建,数据库系统采用MySQL,服务器部署采用Tomcat。 1.2 数…

    Java 2023年5月24日
    00
  • springboot 启动项目打印接口列表的实现

    Spring Boot 启动项目打印接口列表的实现 在本文中,我们将详细讲解如何使用Spring Boot实现在应用程序启动时打印接口列表。我们将介绍两种不同的方法来实现这个目标,并提供示例来说明如何使用这些方法。 方法一:使用Endpoint Spring Boot提供了Endpoint接口,它可以用于公开应用程序的一些信息。我们可以使用这个接口来实现在应…

    Java 2023年5月18日
    00
  • Spring MVC数据处理和乱码问题详解

    以下是关于“Spring MVC数据处理和乱码问题详解”的完整攻略,其中包含两个示例。 Spring MVC数据处理和乱码问题详解 Spring MVC是一个基于Java的Web框架,它可以帮我们快速开发Web应用程序。在使用Spring MVC时,我们需要处理数据和乱码问题。本文将介绍如何处理Spring MVC中的数据和乱码问题。 数据处理 Spring…

    Java 2023年5月17日
    00
  • Springboot轻量级的监控组件SpringbootAdmin

    让我来为你详细讲解一下“Springboot轻量级的监控组件SpringbootAdmin”的完整攻略。 什么是SpringbootAdmin? SpringbootAdmin是一款开源的轻量级的监控组件,它可以实时监控Spring Boot应用程序的状态、指标和环境,同时还可以提供一些管理和监控功能,比如重启应用程序、查看日志等等。 如何使用Springb…

    Java 2023年5月15日
    00
  • java图形界面编程实战代码

    Java图形界面编程是Java中一个重要的领域,Java程序员需要掌握相关技能才能实现优秀的GUI程序。下面是实战Java图形界面编程的完整攻略: 1. 确定开发工具 在开始编写Java图形界面程序之前,程序员需要选择合适的开发工具。常用的Java GUI开发工具包括Swing、JavaFX、AWT等,同时还需要选择Java IDE,如Eclipse、Int…

    Java 2023年5月23日
    00
  • JS实现table表格数据排序功能(可支持动态数据+分页效果)

    这是一篇关于如何使用JavaScript实现table表格数据排序功能的攻略。该攻略可以支持动态数据和分页效果,适用于需要在网站中展示大量表格数据的场景。下面我们将分为以下几部分,详细介绍如何实现此功能: 标题设置(table表格的标题) 通常情况下,table表格都需要设置标题,让用户更好地理解表格中的内容。在HTML中,我们可以通过<th>标…

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