哈夫曼编码实现文件压缩与解压

哈夫曼树的基本介绍

  • 相关概念:

    • 哈夫曼树:n个带权值的叶子结点构成的二叉树,若该树的路径长度(WPL)达到最小

    • 路径长度:L-1(第L层)

    • 带权路径长度:路径长度*该结点权值

    • 树的带权路径长度(WPL):所有叶子结点的带权路径长度之和(权值越大应离根结点越近)

    • WPL最小的就是哈夫曼树

  • 构建哈夫曼树的步骤:

    1. 从小到大进行排序,将每一个数据看作一棵最简单的二叉树

    2. 取出根节点权值最小的两棵二叉树

    3. 组成一棵新的二叉树,该新的二叉树根节点的权值是前面两棵二叉树根节点权值之和

    4. 原列表中加入这棵新树,删去两棵旧树,再重复上述过程,知道只剩下一棵树

  • 哈夫曼树的代码实现

 
import java.util.ArrayList;
import java.util.Collections;

public class HuffmanTree {

    public static void main(String[] args) {
        int arr[] = {1, 4, 2, 3, 66, 44};
        Node root = createHuffmanTree(arr);
        preOrder(root);
    }

    public static Node createHuffmanTree(int[] arr) {
        //第一步为了操作方便
        //1.遍历arr数组
        //2.将arr中的每个元素构成一个Node
        //3.将node放入ArrayList中
        ArrayList<Node> nodes = new ArrayList<>();
        for (int value : arr) {
            nodes.add(new Node(value));
        }
        while (nodes.size() > 1) {
            Collections.sort(nodes);
            //第二步将权值最小两棵树取出组成新树,重复过程
            Node leftNode = nodes.get(0);
            Node rightNode = nodes.get(1);
            Node node = new Node(leftNode.value + rightNode.value);
            node.setLeft(leftNode);
            node.setRight(rightNode);
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(node);
        }
        return nodes.get(0);
    }


    public static void preOrder(Node root){
        if (root==null){
            System.out.println("树空");
            return;
        }else root.preOrder();
    }
}

//让Node实现Comparable接口,实现排序
class Node implements Comparable<Node> {
    int value;//结点权值
    Node left;//指向左子结点
    Node right;//指向右子节点

    public void preOrder() {
        System.out.println(value);
        if (left != null) {
            left.preOrder();
        }
        if (right != null) {
            right.preOrder();
        }
    }

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

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public Node getLeft() {
        return left;
    }

    public void setLeft(Node left) {
        this.left = left;
    }

    public Node getRight() {
        return right;
    }

    public void setRight(Node right) {
        this.right = right;
    }

    @Override
    public int compareTo(Node o) {
        return this.value - o.value;//从小到大排
    }
}
 

哈夫曼编码

  • 基本介绍

    • 哈夫曼编码是一种编码方式,可用于数据压缩,压缩率在20%~90%

    • 哈夫曼编码是可变编码的一种,哈夫曼1952年提出,被称为最佳编码

  • 信息处理原理剖析

    • 定长编码(占用空间较大)

    • 变长编码(空间压缩,但编码出现二义性)

    • 哈夫曼编码(空间压缩且无二义性)

  • 构建哈夫曼编码的过程(例如:处理字符串i like you)

    • 统计每个字符出现的次数,按照字符出现次数构建哈夫曼树

      • Node类中新增char类型表示字符,value表示出现次数

    • 构建哈夫曼树

      • 从小到大进行排序,将每一个数据看作一棵最简单的二叉树

      • 取出根节点权值最小的两棵二叉树

      • 组成一棵新的二叉树,该新的二叉树根节点的权值是前面两棵二叉树根节点权值之和

      • 原列表中加入这棵新树,删去两棵旧树,再重复上述过程,直到只剩下一棵树

    • 根据哈夫曼树,给每个字符规定编码(前缀编码),向左路径为0,右为1

  • 说明与注意

    • 哈夫曼编码是无损处理方案,满足前缀编码,不会造成多义性

    • 根据哈夫曼树排序方法不同,对应的哈弗曼编码也不完全相同,但是WPL是一样的,都是最小的

哈夫曼压缩与解压

  • 数据压缩过程

    • 读取压缩文件对应的字节数组文件

    • 将字节数组元素转成一个个Node结点,将其存放在一个ArrayList集合中,过程用HashMap辅助操作

    • 将得到的ArrayList排序(Node需要提前实现Comparable接口)

    • 根据排好序的结点集合构建哈夫曼树(运用普通的构建哈夫曼树方法)

    • 递归方式得到哈夫曼编码表

    • 将原字节数组依据哈夫曼编码表转换成一个哈夫曼字节数组

      • 先得到二进制的哈夫曼字符串,然后将其转换成字节数组(每八位二进制转成一个字节)

    • 将字节数组和哈夫曼编码表写入新的文件即可

      • 此处无法避免使用对象流(Map编码表难以与字节数组相互转化,还得考虑map和压缩数据的字节数组边界问题),但对象流相比字节流会多出头文件信息,多占用了一点点空间,实测1kb左右,当压缩文件特别小的时候,反向压缩率特别大;但只要文件大小适中,使用字节流和对象流的差距不大,对象流还方便些

      • 但我这里使用的是对象流加字节流此处,对象流直接写入编码表,而数据字节数组使用二进制方式写入(为了界面展示中的进度条动态展示)

  • 数据解压过程

    • 读取哈夫曼编码和对应的编码表

    • 调用相应方法,根据编码表将哈夫曼编码解码得到原来的编码

      • 先将字节数组转化成二进制的字符串 

      • 得到的字符串一位位加大区间去匹配编码表,然后得到解压后的字节数组   

        

哈夫曼编码压缩文件注意事项

  • 如果文件本身就是经过压缩处理的,那么使用哈夫曼编码再次压缩效率不会有明显变化,比如视频,ppt等

  • 哈夫曼编码是按字节来处理的,因此可以处理所有的文件

  • 如果一个文件重复内容不多,压缩效果会不明显

  • 注意二进制字符串和字节数组互转的过程,要考虑到一些细节

当前程序存在的缺陷(有时间再去迭代)

  • 无法处理过大的文件,由于java虚拟机缓存的限制,大文件需要分段处理,而本程序设计之初并未考虑这么多

  • 代码还是比较冗杂,可读性较差

  • ......

完整源代码:

GitHub - FlyingSheep3/-: 通过哈夫曼编码实现文件压缩与解压

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值