哈夫曼樹 java實現構建、查找、生成哈夫曼編碼

哈夫曼樹,又稱最優二叉樹。特點是每一個(葉子)節點都有一個權值,可以認爲代表的是這個點被查詢的概率。哈夫曼樹要求所有(葉子節點)權值*深度的和最短。

爲了說明方便,設節點的值與權值相等。

在這裏插入圖片描述

哈夫曼樹的構建

通過觀察可以看出,權值小的在下,大的在上。由此可以很容易理解構建的規則:
將所有的節點(或者說是隻有根節點的子樹)放入一個集合,每次取出兩個最小的節點合成一個新子樹並放入集合,循環,直到集合中只剩最後一個元素。

jdk已經提供了優先隊列的實現,所以實現起來非常簡單

創建測試代碼,創建節點類

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;

public class HuffmanTree {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<Integer> val = new ArrayList<Integer>();
		
		val.add(2);
		val.add(4);
		val.add(5);
		val.add(7);
		
		HuffmanTree tree = new HuffmanTree();
		Node node = tree.init(val);
		
	}
	
	private class Node{
		Node left;
		Node right;
		Integer value = null;//null代表無值節點
		Integer weight;
		boolean isLeaf;//是否爲葉子節點,所有實際值都在葉子節點上
		public Node(Node left, Node right, Integer value, Integer weight, boolean isLeaf) {
			super();
			this.left = left;
			this.right = right;
			this.value = value;
			this.weight = weight;
			this.isLeaf = isLeaf;
		}
		
	}
	
}

哈夫曼樹構建

	//構建哈夫曼樹
	public Node init(List<Integer> val) {
		Queue<Node> queue = new PriorityQueue<Node>((n1, n2) -> n1.weight - n2.weight);
		val.stream().forEach(v -> queue.add(new Node(null, null, v, v, true)));
		
		while(queue.size() > 1) {
			Node n1 = queue.poll();
			Node n2 = queue.poll();
			queue.add(new Node(n1, n2, null, n1.weight + n2.weight, false));
		}
		
		return queue.poll();
	}

查找

網上相關資料比較少,只看到有一篇文章說了要用廣度優先遍歷。由於權值低的點深度都會相對較低,所以這應該是正確的。

查找

	public boolean has(Node node, Integer value) {
		Queue<Node> queue = new LinkedList();
		queue.add(node);
		
		while(queue.size() > 0) {
			Node temp = queue.poll();
			if(temp.isLeaf && temp.value == value) {
				return true;
			}else {
				if(temp.left != null)
					queue.add(temp.left);
				if(temp.right != null)
					queue.add(temp.right);
			}
		}
		
		return false;
	}

生成哈夫曼編碼

從根節點到目標節點,按照依次經過的是左子樹還是右子樹,分別填0或1 。
一個很直觀的方法是,遍歷每一個葉子節點,再從下向上逆向輸出哈夫曼編碼。但是這就要求節點爲雙向的,改起來有點麻煩。仔細一想,只要用前序遍歷改一下就可以了

	public void printHuffmanCode(Node node) {
		Stack<Integer> stack = new Stack();
		recHuffmanCode(node, stack);
	}
	
	private void recHuffmanCode(Node node, Stack<Integer> stack) {
		if(node == null)return;
		if(node.isLeaf) {
			System.out.println(node.value + ": " + stack);
		}
		stack.add(0);
		recHuffmanCode(node.left, stack);
		stack.pop();
		
		stack.add(1);
		recHuffmanCode(node.right, stack);
		stack.pop();
	}

經過測試後發現,其實中序和後序也是可行的
中序

private void recHuffmanCode_mid(Node node, Stack<Integer> stack) {
		if(node == null)return;
		
		stack.add(0);
		recHuffmanCode(node.left, stack);
		stack.pop();
		
		if(node.isLeaf) {
			System.out.println(node.value + ": " + stack);
		}
		
		stack.add(1);
		recHuffmanCode(node.right, stack);
		stack.pop();
		
	}

最後附上完整代碼

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Stack;

public class HuffmanTree {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<Integer> val = new ArrayList<Integer>();
		
		val.add(2);
		val.add(4);
		val.add(5);
		val.add(7);
		
//		val.add(10);
//		val.add(11);
//		val.add(12);
//		val.add(13);
		
		HuffmanTree tree = new HuffmanTree();
		Node node = tree.init(val);
		
		tree.prevPrint(node);
		
		System.out.println(tree.has(node, 8));
		
		tree.printHuffmanCode(node);
	}
	
	public void printHuffmanCode(Node node) {
		Stack<Integer> stack = new Stack();
		recHuffmanCode_mid(node, stack);
	}
	
	private void recHuffmanCode(Node node, Stack<Integer> stack) {
		if(node == null)return;
		if(node.isLeaf) {
			System.out.println(node.value + ": " + stack);
		}
		stack.add(0);
		recHuffmanCode(node.left, stack);
		stack.pop();
		
		stack.add(1);
		recHuffmanCode(node.right, stack);
		stack.pop();
	}
	
	private void recHuffmanCode_mid(Node node, Stack<Integer> stack) {
		if(node == null)return;
		
		stack.add(0);
		recHuffmanCode(node.left, stack);
		stack.pop();
		
		if(node.isLeaf) {
			System.out.println(node.value + ": " + stack);
		}
		
		stack.add(1);
		recHuffmanCode(node.right, stack);
		stack.pop();
		
	}
	
	public Node init(List<Integer> val) {
		Queue<Node> queue = new PriorityQueue<Node>((n1, n2) -> n1.weight - n2.weight);
		val.stream().forEach(v -> queue.add(new Node(null, null, v, v, true)));
		
		while(queue.size() > 1) {
			Node n1 = queue.poll();
			Node n2 = queue.poll();
			queue.add(new Node(n1, n2, null, n1.weight + n2.weight, false));
		}
		
		return queue.poll();
	}

	public void prevPrint(Node node) {
		if(node == null)return;
		System.out.println("value: " + node.value + ", weight: " + node.weight);
		prevPrint(node.left);
		prevPrint(node.right);
	}
	
	public boolean has(Node node, Integer value) {
		Queue<Node> queue = new LinkedList();
		queue.add(node);
		
		while(queue.size() > 0) {
			Node temp = queue.poll();
			if(temp.isLeaf && temp.value == value) {
				return true;
			}else {
				if(temp.left != null)
					queue.add(temp.left);
				if(temp.right != null)
					queue.add(temp.right);
			}
		}
		
		return false;
	}
	
	private class Node{
		Node left;
		Node right;
		Integer value = null;//null代表無值節點
		Integer weight;
		boolean isLeaf;//是否爲葉子節點,所有實際值都在葉子節點上
		public Node(Node left, Node right, Integer value, Integer weight, boolean isLeaf) {
			super();
			this.left = left;
			this.right = right;
			this.value = value;
			this.weight = weight;
			this.isLeaf = isLeaf;
		}
		
	}
	
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章