Java使用哈夫曼树实现文件压缩的原理及实现

本文介绍了哈夫曼树的概念及其在文件压缩中的应用。通过构造哈夫曼树并为每个字符分配01编码,实现无损压缩。在哈夫曼压缩过程中,频繁出现的字符用较短编码,降低文件存储空间。文章详细阐述了哈夫曼树的构造步骤、编码方法以及在Java中实现压缩的流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.哈夫曼树

哈夫曼树又称最优树(二叉树),是一类带权路径最短的树。构造这种树的算法最早是由哈夫曼(Huffman)1952年提出,这种树在信息检索中很有用。

结点之间的路径长度:从一个结点到另一个结点之间的分支数目。

树的路径长度:从树的根到树中每一个结点的路径长度之和。

 

结点的带权路径长度:从该结点到树根之间的路径长度与结点上权的乘积。

树的带权路径长度:树中所有叶子结点的带权路径长度之和,记作:

 

    WPL为最小的二叉树就称作最优二叉树或哈夫曼树。

    完全二叉树不一定是最优二叉树。

2. 哈夫曼树的构造

哈夫曼树的构造方式如下:

(1)根据给定的n个权值{w1,w2,...,wn}构造n棵二叉树的集合F={T1,T2,...,Tn},其中Ti中只有一个权值为wi的根结点,左右子树为空;

(2)在F中选取两棵根结点的权值为最小的数作为左、右子树以构造一棵新的二叉树,且置新的二叉树的根结点的权值为左、右子树上根结点的权值之和。

(3)将新的二叉树加入到F中,删除原两棵根结点权值最小的树;

(4)重复(2)和(3)直到F中只含一棵树为止,这棵树就是哈夫曼树。

例1:

例2:

 

3. 哈夫曼树编码

  从哈夫曼树根结点开始,对左子树分配代码“0”,右子树分配代码“1”,一直到达叶子结点为止,然后将从树根沿每条路径到达叶子结点的代码排列起来,便得到了哈夫曼编码。

例,对字符串 EMCAD 编码。若等长编码,则:

 

   EMCAD => 000001010011100 共15位。

设各字母的出现频率为 {E,M,C,A,D}={1,2,3,3,4}。以频率为权值生成哈夫曼树,并在叶子上标注对应的字母,树枝分配代码“0”或“1”:

 

各字母的编码即为哈夫曼编码:  EMCAD => 000001011011 共12位

4.哈夫曼压缩的实现原理

   随着网络与多媒体技术的兴起,人们需要存储和传输的数据越来越多,数据量越来越大,以前带宽有限的传输网络和容量有限的存储介质难以满足用户的需求。 特别是声音、图像和视频等媒体在人们的日常生活和工作中的地位日益突出,这个问题越发显得严重和迫切。如今,数据压缩技术早已是多媒体领域中的关键技术之一。

4.1什么是哈弗曼压缩

  Huffman( 哈夫曼) 算法在上世纪五十年代初提出来了,它是一种无损压缩方法,在压缩过程中不会丢失信息熵,而且可以证明 Huffman 算法在无损压缩算法中是最优的。 Huffman 原理简单,实现起来也不困难,在现在的主流压缩软件得到了广泛的应用。对应用程序、重要资料等绝对不允许信息丢失的压缩场合, Huffman 算法是非常好的选择。

4.2哈弗曼压缩实现

   哈夫曼压缩是个无损的压缩算法,一般用来压缩文本和程序文件。哈夫曼压缩属于可变代码长度算法一族。意思是个体符号(例如,文本文件中的字符)用一个特定长度的位序列替代。因此,在文件中出现频率高的符号,使用短的位序列,而那些很少出现的符号,则用较长的位序列。

压缩的时候当我们遇到了前面示例中的文本E、M、C、A、D几个字符的时候,我们不用原来的存储,而是转化为用它们的 01 串来存储不久是能减小了空间占用了吗。(什么 01 串不是比原来的字符还多了吗?怎么减少?)大家应该知道的,计算机中我们存储一个字节型数据的时候一般式占用了8 个 01 位,因为计算机中所有的数据都是最后转化为二进制位去存储的。所以,想想我们的编码不就是只含有 0 和 1 了,因此我们就直接将编码按照计算机的存储规则用位的方法写入进去就能实现压缩了。

下面开始动手开发我们自己的压缩软件。

开始些程序之前,必须想好自己的文件存储格式,和存储规则是什么 

为了简便,我自定义存储的基本信息,格式如下: 

SaveCode[i].n    int型   // 每一个字节的编码长度  i:0~256 

B yte[]   byte数组型 // 每一个字节的编码 SaveCode[i].node 中 String 的 01 串转化而来。

 Byte[]  byte数组型 // 对文件中每一个 byte 的重新编码的哈夫曼编码写入。

 

①将要压缩的文件一个一个字节的读出来即扫描要压缩的文件,并统计每个字节的权值即出现的频率。

// 创建文件输入流   

        java.io.FileInputStream fis = new java.io.FileInputStream(path);   

        //读入所有的文件字节   

        while(fis.available()>0){   

            int i=fis.read();   

            byteCount[i]++;   

        }

 

②构建哈夫曼树:

这里我们采用优先队列的方法,因为优先队列比较符合哈夫曼的构造方法,形式上也非常的相似。

/**  

 * 使用优先队列构建哈弗曼树  

*/  

public void createTree(){   

//优先队列   

 PriorityQueue<hfmNode> nodeQueue = new PriorityQueue<hfmNode>();   

//把所有的节点都加入到 队列里面去   

 for (int i=0;i<byteCount

综合实验: 1. 问题描述 利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。这要求在发送端通过一个编码系统对待传输数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站编写一个哈夫曼码的编/译码系统。 2. 基本要求 一个完整的系统应具有以下功能: (1) I:初始化(Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼,并将它存于文件hfmTree中。 (2) E:编码(Encoding)。利用已建好的哈夫曼(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。 (3) D:译码(Decoding)。利用已建好的哈夫曼文件CodeFile中的代码进行译码,结果存入文件Textfile中。 (4) P:印代码文件(Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件CodePrin中。 (5) T:印哈夫曼(Tree printing)。将已在内存中的哈夫曼以直观的方式(比如)显示在终端上,同时将此字符形式的哈夫曼写入文件TreePrint 中。 3. 测试数据 用下表给出的字符集和频度的实际统计数据建立哈夫曼,并实现以下报文的编码和译码:“THIS PROGRAME IS MY FAVORITE”。 字符 A B C D E F G H I J K L M 频度 186 64 13 22 32 103 21 15 47 57 1 5 32 20 字符 N O P Q R S T U V W X Y Z 频度 57 63 15 1 48 51 80 23 8 18 1 16 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值