文件压缩(一)——Huffman树的构建

在传输文件的过程中我们经常会先对文件进行压缩,然后再传输。等接收到文件后,在进行解压缩。于是也打算自己动手写一个简单的文件压缩程序,大致分为三个过程,Huffman树的构建,利用哈夫曼树对输入的字符串进行压缩和解压缩处理,文件处理。今天,我们先来实现Huffman树的构建。在动手写代码之前我们先做一些准备工作。(这里不会详细介绍Huffman树,对Huffman基本概念不了解的同学请自行查阅其他资料)

一、Huffman树是什么

Huffman树又被称为“最优二叉树”。它是带权路径长度最短的树,权值较大的结点离根较近(这样子,出现频率较高的节点相应的编码长度就会越短)。

Huffman树的一个应用就是压缩文件。在计算机中字符所采用的编码是Ascii码,总共有八个bit位,可以表示256个字符。但是在实际生活中,我们的文本值用到这些字符当中的一小部分。那么此时这种编码方式就显得很浪费空间,传输起来肯定就比较慢。于是我们就想利用Huffman树重新对这些字符进行编码,以减少传输文本的大小,提高传输的速度。

二、构建思路(压缩文本以英文为例)

1.节点类需要包含哪些信息

A.数据(这里是字符);B.权值;C.左孩子节点;D.右孩子节点;E.Huffman编码

2.构建节点类

遍历带压缩的字符串,为出现的所有字符构建节点类,添加到节点数组nodes中;

3.构建树

遍历节点数组,每次都找出其中权值最小的两个节点,并为他们构建一个父节点,这个父节点的权值是这两个节点的权值之和,数据为空,左孩子是权值较小的那个节点,右孩子是权值较大的那个节点。终止条件是当nodes中只剩一个节点时。

4.获取字符编码

根据所构建的树,翻译出各个字符所对应的Huffman编码。在建树结束之后再去遍历整棵树,然后得到相应的编码。我这里的为每个节点新增了一个编码code,这个code被初始化为"”。这样我们在从根节点往下遍历时,遇到根节点的左孩子节点,就设置它的编码为其父节点的编码+“0”;右孩子节点,则设置它的编码为其父节点的编码+“1”,最后我们再把其中的叶节点的字符和编码添加到HashMap里面。

三、具体代码

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Queue;

//构造Huffman树
public class Huffman {
	//构造节点类
	public static class Node<E>{
		E data;//数据,这里是字符
		double weight;//权值
		String code="";
		Node leftChild;
		Node rightChild;
		
		public Node(E data,double weight) {
			this.data=data;
			this.weight=weight;
		}
	}
	
	public static void main(String[] args) {
		List<Node> nodes=new ArrayList<Node>();
		//这里只能用泛型的类型数据,比如int只能用Integer,这里的char只能用Character来代替
		HashMap<Character,String> map=new HashMap<Character,String>();
		
		nodes.add(new Node('a',40.0));
		nodes.add(new Node('b',8.0));
		nodes.add(new Node('c',10.0));
		nodes.add(new Node('d',30.0));
		nodes.add(new Node('e',10.0));
		nodes.add(new Node('f',2.0));
		
		Node root=Huffman.creatTree(nodes);//调用建树函数
		//打印出所有的节点
		List<Node> list=new ArrayList<Node>();
		list=Huffman.breadthFirst(root);
		
		for(int i=0;i<list.size();i++) {
			//判断字符是否存在,若存在则将其加入到Hashmap中
			if(list.get(i).data!=null) {
				map.put((Character) list.get(i).data, list.get(i).code);
			}
		}
		System.out.println(map);
	}
	
	/*
	 * 构造哈夫曼树树
	 * 通过插入节点数组来建树
	 */
	//建树函数
	public static Node creatTree(List<Node>nodes) {
		//判断数组中是否还有两个以上的节点
		while(nodes.size()>1) {
			Node minleft=nodes.get(0);
			int position=0;
			for(int i=1;i<nodes.size();i++)
				if(minleft.weight>nodes.get(i).weight) {
					 minleft=nodes.get(i);//找到最小值
					 position=i;
				}
			nodes.remove(position);//移除最小值
			
			Node minright=nodes.get(0);
			position=0;
			for(int i=1;i<nodes.size();i++) {
				if(minright.weight>nodes.get(i).weight) {
					minright=nodes.get(i);//找到第二个最小值
					position=i;
				}
			}
			nodes.remove(position);//移除最小值
			
			Node parent=new Node(null,minleft.weight+minright.weight);//构造父节点
			//设置孩子节点的指针
			parent.leftChild=minleft;
			parent.rightChild=minright;
			//把新建的父节点加到数组中
			nodes.add(parent);
		}
		return nodes.get(0);
	}
	
	//从根节点往下走,获取各个叶子结点的编码
	public static List<Node> breadthFirst(Node root){
		Queue<Node> queue=new ArrayDeque<Node>();//创建一个队列
		List<Node> list=new ArrayList<Node>();//创建一个数组
		
		//如果根节点不为空,就将它加入队列
		if(root!=null) {
			queue.offer(root);
		}
		while(!queue.isEmpty()) {
			//将队位元素加到list数组中
			list.add(queue.peek());
			/*
			 * java 堆栈中的方法poll和pop区别如下:
             *pop:相当于get的操作,就是只是查看。从此列表所表示的堆栈处弹出一个元素。
             *poll:相当于先get然后再remove掉,就是查看的同时,也将这个元素从容器中删除掉。 获取并移除此列表的头(第一个元素)
			 */
			Node p=queue.poll();
			
			//如果左孩子节点不为null,则入队列
			if(p.leftChild!=null) {
				//左孩子节点的编码等于父节点+0
				p.leftChild.code=p.code+"0";
				queue.offer(p.leftChild);
			}
			
			//如果右孩子节点不为空,则右孩子节点入队列
			if(p.rightChild!=null) {
				//右孩子节点的编码等于父节点+1
				p.rightChild.code=p.code+"1";
				queue.offer(p.rightChild);
			}
			
		}
		return list;	
	}
	
}

四、运行结果


五、总结反思

1.<>里面只能放泛型数据类型,而不能放具体的数据类型。比如int就只能用Integer,char只能用Character

2. java 堆栈中的方法poll和pop区别:
pop:相当于get的操作,就是只是查看。从此列表所表示的堆栈处弹出一个元素。

poll:相当于先get然后再remove掉,就是查看的同时,也将这个元素从容器中删除掉。 获取并移除此列表的头(第一个元素)

3.String最好初始为=“”;如果初始为null,后面如果要把这个字符串和其他字符串相连接,比如data=null,那么data+"12"就会变成“null12”

猜你喜欢

转载自blog.csdn.net/alexwym/article/details/80904308
今日推荐