1. 程式人生 > >最優二叉樹(赫夫曼樹)的構建

最優二叉樹(赫夫曼樹)的構建

一、構建最優二叉樹

①、節點類:五個屬性:結點的資料、父結點、左子結點、右子結點、赫夫曼編碼

/**
 * 樹的結點類
 * 
 * @author lenovo
 * 
 */
public class TreeNode {

	private Object obj; // 資料
	private TreeNode parent; // 父結點
	private TreeNode leftchild; // 左子結點
	private TreeNode rightchild; // 右子結點
	private String code;	//赫夫曼編碼
	
	/**
	 * 重寫構造方法
	 * @param obj
	 */
	public TreeNode(Object obj){
		this.obj=obj;
	}

	public Object getObj() {
		return obj;
	}

	public void setObj(Object obj) {
		this.obj = obj;
	}

	public TreeNode getParent() {
		return parent;
	}

	public void setParent(TreeNode parent) {
		this.parent = parent;
	}

	public TreeNode getLeftchild() {
		return leftchild;
	}

	public void setLeftchild(TreeNode leftchild) {
		this.leftchild = leftchild;
	}

	public TreeNode getRightchild() {
		return rightchild;
	}

	public void setRightchild(TreeNode rightchild) {
		this.rightchild = rightchild;
	}
	
	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	
}

②、構建最有二叉樹的思路:最優二叉樹即整棵樹的總權值最小,只有每個葉結點才存放資料。先建立一個佇列(優先佇列,已經排好序),根據陣列中的資料建立結點存放到佇列中。然後對佇列進行構樹操作,每次從優先佇列中取出結點(用poll方法),分別設為左子結點、右子結點,再將左子結點和右子結點的資料相加併為父結點,再把父結點放到優先佇列中去,再取出兩個結點,分別設為左子結點、右子結點,再將左子結點和右子結點的資料相加併為父結點,如此迴圈,去兩個放一個,直到優先佇列中的只剩下一個結點,再將最後一個結點取出,設為最終的根結點。

/**
 * 實現比較器的類
 * @author lenovo
 *
 */

public class MyComparator implements Comparator<TreeNode>{

	/**
	 * 比較TreeNode中兩個obj的大小,o1.getObj()>o2.getObj()返回1(從大到小排序),小於返回-1(從小到大排序)
	 */
	@Override
	public int compare(TreeNode o1, TreeNode o2) {
		
		return (Integer)o1.getObj()-(Integer)o2.getObj();
	}

}
 
/**
	 * 將陣列轉化為最優二叉樹的方法
	 * 
	 * @param array
	 * @return:最優二叉樹的根節點
	 */
	public TreeNode arraytoTree(int[] array) {

		if (array == null) {
			throw new RuntimeException("陣列為空");
		}

		// 定義優先佇列一個佇列,指定初始長度和排序方式
		java.util.PriorityQueue<TreeNode> pqueue = new java.util.PriorityQueue<TreeNode>(
				array.length, new MyComparator());

		// 遍歷陣列,得到陣列中的元素,建立TreeNode物件,加入到優先佇列中去
		for (int i = 0; i < array.length; i++) {
			
			TreeNode tree = new TreeNode(array[i]);
			pqueue.add(tree);
		}

		// 如果優先佇列中還有元素,構建赫夫曼樹
		while (pqueue.size() > 1) {
			TreeNode ltree = pqueue.poll(); // 得到左子結點,poll()方法,取出列表頭並移除
			TreeNode rtree = pqueue.poll(); // 得到右子結點

			// 建立根節點
			TreeNode root = new TreeNode((Integer) ltree.getObj()
					+ (Integer) rtree.getObj());
			// 設定關係
			root.setLeftchild(ltree);
			root.setRightchild(rtree);
			ltree.setParent(root);
			rtree.setParent(root);

			// 加入子結點
			pqueue.add(root);
		}
		//得到根結點
		TreeNode root = pqueue.peek();

		return root;
	}
 

二、對葉節點進行赫夫曼編碼

從根結點開始,在父結點左邊的編碼為0,在父結點右邊的編碼為1,直到葉節點,即可以的得到每個葉節點的赫夫曼編碼。

/**
	 * 設定並列印葉結點的赫夫曼編碼 左:0; 右:1
	 * 
	 * @param root
	 *            :根結點
	 */
	public void printCode(TreeNode root) {
		// 如果為葉節點
		if (root.getLeftchild() == null && root.getRightchild() == null) {
			System.out.println("結點:" + root.getObj());
			System.out.println("\t\t赫夫曼編碼:"+ root.getCode());
		}

		// 左子結點為0
		if (root.getLeftchild() != null) {
			String s = root.getCode();
			if(s==null){	//如果是根結點的子左結點
				root.getLeftchild().setCode("0");
			}else{
				root.getLeftchild().setCode(s+'0');
			}
			printCode(root.getLeftchild()); // 遞迴
		}
		// 右子結點為1
		if (root.getRightchild() != null) {
			String s = root.getCode();
			if (s ==null) {	//如果是根結點的子右結點
				root.getRightchild().setCode( "1");
			}else{
				root.getRightchild().setCode( s+'1');
			}
			printCode(root.getRightchild()); // 遞迴
		}

	}