霍夫曼树浅析

在学习FP-Tree时发现,它应用的思想和huffman压缩的思想特别像,所以先复习了一遍huffman树。

霍夫曼树是一种常见的数据压缩算法,通过字符出现频率和二叉树来进行的压缩。
比如,要压缩这个字符串:
         “beep boop beer!”

step 1:获取每个字符的出现频率 
霍夫曼树浅析
step 2:按次数顺序放入数据,若次数相同,则按照出现的顺序

step 3:接下来就是我们的算法——把这个数组转成二叉树。我们始终从数组头取两个元素来构造一个二叉树(第一个元素是左结点,第二个是右结点),并把这两个元素的次数相加,并放回数组中,然后,按照顺序再次排序:
霍夫曼树浅析
go on
霍夫曼树浅析霍夫曼树浅析

霍夫曼树浅析霍夫曼树浅析
最终得到的树为:
霍夫曼树浅析
此时,我们把这个树的左支编码为0,右支编码为1,这样我们就可以遍历这棵树得到字符的编码,比如:‘b’的编码是 00,’p’的编码是101, ‘r’的编码是1000。我们可以看到出现频率越多的会越在上层,编码也越短,出现频率越少的就越在下层,编码也越长
霍夫曼树浅析
最终我们可以得到下面这张编码表:
霍夫曼树浅析

于是,对于我们的原始字符串  beep boop beer!

其二进制为 : 0110 0010 0110 0101 0110 0101 0111 0000 0010 0000 0110 0010 0110 1111 0110 1111 0111 0000 0010 0000 0110 0010 0110 0101 0110 0101 0111 0010 0010 0001

我们的Huffman的编码为: 0011 1110 1011 0001 0010 1010 1100 1111 1000 1001

从上面的例子中,我们可以看到被压缩的比例还是很可观的。

java实现为:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author mazhiyuan
 * 
 */
public class Huffman {
	String encode(String input) {
		char[] c = input.toCharArray();
		Map<Character, Node> ci = new HashMap<Character, Node>();
		int i = 0;
		for (char _c : c) {
			if (ci.get(_c) == null)
				ci.put(_c, new Node(_c, null, null, 1, ++i));
			else
				ci.get(_c).add();
		}

		List<Node> datas = new ArrayList<Node>(ci.values());
		sort(datas);
		build(datas);
		Map<Character, String> cs = encoding(datas.get(0));
		StringBuilder sb = new StringBuilder();
		for (char _c : c)
			sb.append(cs.get(_c)).append(" ");
		return sb.toString();
	}

	private void sort(List<Node> nodes) {
		Collections.sort(nodes, new Comparator<Node>() {

			@Override
			public int compare(Node o1, Node o2) {
				// TODO Auto-generated method stub
				return o1.weight - o2.weight == 0 ? o1.no - o2.no : o1.weight
						- o2.weight;
			}

		});
	}

	private void build(List<Node> datas) {
		while (datas.size() > 1) {
			sort(datas);
			Node n1 = datas.get(0);
			Node n2 = datas.get(1);
			Node n = new Node(null, n1, n2, n1.weight + n2.weight,
					n1.no > n2.no ? n2.no : n1.no);
			datas.remove(0);
			datas.remove(0);
			datas.add(n);
		}
	}

	private Map<Character, String> encoding(Node root) {
		Map<Character, String> map = new HashMap<Character, String>();
		process(map, root, "");
		return map;
	}

	private void process(Map<Character, String> map, Node node, String content) {
		if (node.left == null) {
			map.put(node.item, content);
			return;
		}
		process(map, node.left, content + "0");
		process(map, node.right, content + "1");
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Huffman h = new Huffman();
		String str = "woaibeijingtiananmen";
		System.out.println(h.encode(str));
		byte[] bs = str.getBytes();
		for (byte b : bs)
			System.out.print(Integer.toBinaryString(b)+" ");
	}

}

// 树结点
class Node {
	Character item;
	Node left;
	Node right;
	int weight;
	int no;

	Node(Character data, Node left, Node right, int weight, int no) {
		this.item = data;
		this.weight = weight;
		this.left = left;
		this.right = right;
		this.no = no;
	}

	void add() {
		weight += 1;
	}

	@Override
	public String toString() {
		return "Node [item=" + item + ", left=" + left + ", right=" + right
				+ ", weight=" + weight + "]";
	}
}
输出结果:
10110 10111 110 111 0000 001 111 0001 111 01 1000 1001 111 110 01 110 01 1010 001 01 
1110111 1101111 1100001 1101001 1100010 1100101 1101001 1101010 1101001 1101110 1100111 1110100 1101001 1100001 1101110 1100001 1101110 1101101 1100101 1101110



你可能感兴趣的:(java,Huffman,霍夫曼,霍夫曼树)