042.哈夫曼编码的原理和实现

本文详细介绍了哈夫曼编码的基本原理,包括编码方式中的定长编码、变长编码和哈夫曼编码。通过示例展示了哈夫曼编码的构建过程,包括字符出现频率统计、哈夫曼树构建、编码生成等步骤。同时,提供了哈夫曼编码的Java实现,包括结点类、编码和解码方法,并给出了测试结果,显示了压缩和解压缩的全过程。

博主的 Github 地址


1. 哈夫曼编码的原理

1.1. 基本介绍

  • 哈夫曼编码(Huffman Coding)是一种编码方式, 属于一种程序算法
  • 哈夫曼编码是哈夫曼树在电讯通信中经典应用之一
  • 哈夫曼编码广泛地用于数据文件解缩压缩. 压缩率通常在 20-90% 之间
  • 哈夫曼编码是可变字长编码(VLC)的一种.
  • 是哈夫曼于 1952 年提出一种编码方法, 称之为最佳编码.

1.2. 编码方式

通讯领域中信息处理的方式通常有 3 种

1.2.1. 定长编码

存在着占用太多位的问题
pic1

1.2.2. 变长编码

存在着解码可能产生二义性的问题
pic2

1.2.3. 哈夫曼编码

pic3

1.3. 原理剖析

以上述案例来进行示范

1.3.1. 编码步骤
  • 首先用上述例子的字符串进行传输, 即字符串

    “i like like like java do you like a java”

  • 然后确定各个字符出现的次数, 如下:

    “d”:1 “y”:1 “u”:1 “j”:2 “v”:2 “o”:2
    “l”:4 “k”:4 “e”:4 “i”:5 “a”:5 " ":9

  • 按照上面字符出现的次数构建一棵哈夫曼树, 次数作为权值.
    HuffmanTree

  • 然后每个左右指针分别做标识, 左指针为"0", 右指针为"1".
    Sign

  • 最后依次得出每个字母的哈夫曼编码的编码序列.
    这个编码属于前缀编码,各个编码间的前缀并不会重复.

    字符 编码 字符 编码 字符 编码 字符 编码 字符 编码
    " " 01 “l” 001 “j” 0000 “u” 10010 “d” 100110
    - - “i” 101 “v” 0001 - - “y” 100111
    - - “a” 110 “o” 1000 - - - -
    - - - - “k” 1110 - - - -
    - - - - “e” 1111 - - - -
  • 按照上面的哈夫曼编码, 最终可将字符串转换成如下编码
    (这里使用无损压缩, 得到的编码长度为 133)

    1010100110111101111010011011110111101001
    1011110111101000011000011100110011010000
    1100111100010010010011011110111101110000
    01100001110

  • 得到的最终编码进行匹配解码的时候,
    可直接按照前缀来逐段比较, 因为前缀不重复,
    不会造成匹配二义性, 所以最终必定将全部编码匹配完成.
    原来用定长编码的长度是 359, 压缩率算出为 (359-133)/359=0.629

  • 注意:
    以上例子可能会根据哈夫曼排序方法不同, 得出编码不一样.
    因为存在多个权值相同的结点, 其排序顺序可能并不一样.
    但是排序得到的哈夫曼树的 WPL 是一样的, 都是最小的.
    因此最终得到的编码长度都是相同的, 并无影响.

2. 哈夫曼编码的实现

2.1. 实现思路

  1. 先创建一个结点类, 结点成员有四个, 分别是:
    data 存放字符对应的 ASCII 码, weight 存放权值,
    left 和 right 存放左右子结点.

  2. 得到目标字符串对应的 byte[] 数组, 用以统计得出权值.

  3. 编写方法, 将准备构建哈夫曼树的结点放到 List 中.

  4. 通过构建的 List 来创建对应的哈夫曼树.

2.2. 代码实现编码和解码

  • 实现细节均在代码注释当中
2.2.1. 结点类
package com.leo9.dc27.huffman_code;
//创建结点类, 用以存放每个字符的数据
public class TreeNode implements Comparable<TreeNode>{
   
   
    //存放字符的数据
    Byte char_data;
    //存放字符的出现次数, 即结点的权值
    int char_weight;
    //定义左右子结点
    TreeNode left_node;
    TreeNode right_node;

    //定义构造函数
    public TreeNode(Byte char_data, int char_weight) {
   
   
        this.char_data = char_data;
        this.char_weight = char_weight;
    }

    @Override
    //定义排序
    public int compareTo(TreeNode o) {
   
   
        //从小到大排序
        return this.char_weight - o.char_weight;
    }

    @Override
    public String toString() {
   
   
        return "TreeNode{" +
                "char_data=" + char_data +
                ", char_weight=" + char_weight +
                '}';
    }

    //前序遍历结点
    public void preOrder(){
   
   
        System.out.println(this);
        if(this.left_node != null){
   
   
            this.left_node.preOrder();
        }
        if(this.right_node != null){
   
   
            this.right_node.preOrder();
        }
    }
}


2.2.2. 哈夫曼编码类
package com.leo9.dc27.huffman_code;

import javafx.print.Collation;

import java.util.
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值