您的位置:首页 > 其它

哈夫曼压缩

2015-08-31 11:31 253 查看
   此文主要分析的是哈夫曼压缩的重点包括统计字符频率,建哈夫曼树,生成码表。哈夫曼压缩是最常用的一种静态无痕压缩。

   以前也学习过哈夫曼的算法结构,但是没有自己去写代码实现,这次再学习了一遍,更加深刻理解哈夫曼压缩的原理,如何真正实现文件的压缩节省内存资源。下面梳理下我的代码和分析逻辑。

   第一步是打开文件,读取文件流,统计文字频率。

   方法是读取文件内容,根据每个字符有唯一的字节,存储在长度为256的数组中。可将字符和频率绑定为一个节点类Node,所有节点类的对象存储在队列中。

/*
* 统计频率
*/
private static Mylist<Node> countFre(String path) throws IOException {
// 创建一个文件输入流对象
java.io.FileInputStream fis = new java.io.FileInputStream(path);
java.io.BufferedInputStream bis = new java.io.BufferedInputStream(fis);
int[] byteArray = new int[256];
while (bis.available() > 0) {
int i = bis.read();
// 统计字符的频率
byteArray[i]++;
}
// 关闭文件输入流
fis.close();

Mylist<Node> l = new Mylist<Node>();
// 遍历数组,打印并保存
for (int i = 0; i < byteArray.length; i++) {
if (byteArray[i] != 0) {
char c = (char) i;
int count = byteArray[i];
// System.out.println("字符:"+c+"   "+"频率:"+count);
// 添加到队列
l.add(new Node(c, count));
}
}
return l;
}


  第二步是排序。

   根据字符所对应的频率由小到大进行排序,可用选择排序算法。

/*
* 排序
*/
private static void sort(Mylist<Node> list) {
for (int i = 0; i < list.size() - 1; i++) {
int min = i;
for (int j = i + 1; j < list.size(); j++) {
// 如果j返回的节点中的频率大于i
if (list.get(j).getFrequency() < list.get(min).getFrequency())
min = j;
}
// 交换
if (i != min) {
Node n = list.get(min);
list.set(list.get(i), min);
list.set(n, i);
}
}

}


第三步是构建哈夫曼二叉树。

   依次取出排好序的节点,每次取出前两个生成一个父节点,再将父节点添加到队列中,这里我将生成父节点的过程写在另一个函数creatHFM里,方便理解逻辑。

/*
* 构建哈夫曼二叉树
*/
private static Node creat(Mylist<Node> list){
while (list.size() > 1) {
Node l = list.remove(0);
Node r = list.remove(0);
Node n = creatHFM(l, r);
// 把新的节点添加到队列中
list.add(n);
// 排序
sort(list);
}
//根节点
Node root = list.get(0);
return root;
}
private static Node creatHFM(Node left, Node right) {
int fc = left.getFrequency() + right.getFrequency();
Node root = new Node(' ', fc);
root.setLeft(left);
root.setRight(right);
return root;
}


   第四步是给每个字符编码,生成码表。

  用递归遍历的方法生成每一个字符的编码,左0右1,并绑定字符保存,即新建一个类,依次保存到队列中。

/*
* 计算编码
*/
private static void creatCode(Node root, String str) {
// 若不是叶节点,递归遍历
if (root.getLeft() != null) {
creatCode(root.getLeft(), str + "0");
creatCode(root.getRight(), str + "1");
} else {// 否则输出,编码,并保存
count += root.getFrequency() * str.length();
System.out.println(root.getC() + " = " + str);
// 保存每一个字符及其编码,存入队列
Character chact = new Character(root.getC(), str);
codelist.add(chact);
}

}


   在以上代码中所有的方法的参数输入由上一个方法的返回决定,更加方便梳理逻辑,查错。

  

   构造方法及主函数,其中主函数中测试文件中的内容为string

public HFMtree(String path) throws IOException {
// 统计每个字符的频率
list = countFre(path);

// 根据每个字符的频率排序
sort(list);
// 构建哈夫曼二叉树,返回根节点
Node root = creat(list);
// 生成每一个字符的编码及带权路径。并保存编码
creatCode(root, "");
}

/*
* 主函数
*/
public static void main(String[] args) throws IOException {
//		文件路径
String str = "F:/text.txt";
//		文件的内容
//		String string = "jfsjflvyavllcyqlcjhqvYV";
HFMtree hfm=new HFMtree(str);
System.out.print("带权路径=" + count);

}


测试结果







大小: 2.5 KB

查看图片附件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: