Java實現特殊二叉樹之堆、哈夫曼樹


什麼是:堆是一種經過排序的完全二叉樹,其中任一非終端節點的數據值均不大於(或不小於)其左孩子和右孩子節點的值;

       首先,堆分爲大根堆(最大堆)和小根堆(最小堆)二種;

       小根堆滿足:

               (1)若根結點存在左孩子,則根結點的值小於等於左孩子結點的值;

               (2)若根結點存在右孩子,則根結點的值小於等於右孩子結點的值;

               (3)以左右孩子爲根的子樹又各是一個堆;

       或者:

               根結點(亦稱爲堆頂)的關鍵字是堆裏所有結點關鍵字中最小者的堆稱爲小根堆


       大根堆與小根堆類似,只需把小於等於改爲大於等於就得到了;

如:





哈夫曼樹:又稱最優二叉樹,是一種帶權長度最短的二叉樹,所謂樹的帶權路徑長度,就是樹中所有的葉結點的權值乘上其到根結點的路徑長度;


樹的帶權路徑長度記爲:    WPL=(21*L1+W2*L2+W3*L3+...+Wn*Ln);

如:給定4個葉子結點a,b,c,d,分別帶權7,4,5,2,構造如下面所示的三棵二叉樹(還有許多種),它們的帶權路徑長度分別爲:

(1)WPL = 7*2+5*2+2*2+4*2;

(2)WPL = 7*1+5*2+2*3+4*3;

(3)WPL = 7*3+5*3+2*1+4*2;


其中(2)的WPL最小,可以驗證,它就是哈夫曼樹;

因此,在n個帶權葉子結點所構成的二叉樹中,滿二叉樹或完全二叉樹不一定是最優二叉樹,權值越大的結點離根越近的二叉樹纔是最優二叉樹;


構造哈夫曼樹:




哈夫曼編碼:

求出哈夫曼樹後,以上圖爲例,只需人爲規定左側爲0,右側爲1,那麼結點9的編碼是:00;結點5的編碼是:10。哈夫曼編碼是一種應用廣泛且非常有效的數據壓縮技術,該技術一般可將數據文件壓縮掉20%-90%,其壓縮效率取決於被壓縮文件的特徵;


Java代碼:

package huffman;

/**
 * 
 * 二叉樹節點
 */

class Node implements Comparable
{
	private int value;
	private Node leftChild;
	private Node rightChild;

	public Node(int value)
	{
		this.value = value;
	}

	public int getValue()
	{
		return value;
	}

	public void setValue(int value)
	{
		this.value = value;
	}

	public Node getLeftChild()
	{
		return leftChild;
	}

	public void setLeftChild(Node leftChild)
	{
		this.leftChild = leftChild;
	}

	public Node getRightChild()
	{
		return rightChild;
	}

	public void setRightChild(Node rightChild)
	{
		this.rightChild = rightChild;
	}

	public int compareTo(Object o)
	{
		Node that = (Node) o;
		double result = this.value - that.value;
		return result > 0 ? 1 : result == 0 ? 0 : -1;
	}
}
package huffman;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class HuffmanTreeBuilder
{
	public static void main(String[] args)
	{
		// 方式1
		List<Node> list = new ArrayList<Node>();
		list.add(new Node(7));
		list.add(new Node(5));
		list.add(new Node(4));
		list.add(new Node(2));

		// 方式2
		List<Node> nodes = Arrays.asList(new Node(1), new Node(3), new Node(5),
				new Node(7));

		Node node = HuffmanTreeBuilder.build(list);
		PrintTree(node);
	}

	/**
	 * 
	 * 構造哈夫曼樹
	 * 
	 */
	private static Node build(List<Node> nodes)
	{
		nodes = new ArrayList<Node>(nodes);
		sortList(nodes);
		while (nodes.size() > 1)
		{
			createAndReplace(nodes);
		}
		return nodes.get(0);
	}

	/**
	 * 
	 * 組合兩個權值最小結點,並在結點列表中用它們的父結點替換它們
	 * 
	 */
	private static void createAndReplace(List<Node> nodes)
	{
		Node left = nodes.get(0);
		Node right = nodes.get(1);
		Node parent = new Node(left.getValue() + right.getValue());
		parent.setLeftChild(left);
		parent.setRightChild(right);
		nodes.remove(0);
		nodes.remove(0);
		nodes.add(parent);
		sortList(nodes);
	}

	/**
	 * 
	 * 將結點集合由大到小排序
	 */
	@SuppressWarnings("unchecked")
	private static void sortList(List<Node> nodes)
	{
		Collections.sort(nodes);
	}

	/**
	 * 
	 * 打印樹結構,顯示的格式是node(left,right)
	 * 
	 */
	public static void PrintTree(Node node)
	{
		Node left = null;
		Node right = null;
		if (node != null)
		{
			System.out.print(node.getValue());
			left = node.getLeftChild();
			right = node.getRightChild();
			System.out.println("(" + (left != null ? left.getValue() : " ")
					+ "," + (right != null ? right.getValue() : " ") + ")");
		}

		if (left != null)
			PrintTree(left);

		if (right != null)
			PrintTree(right);

	}

}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章