最優二叉樹(赫夫曼樹)的構建
阿新 • • 發佈:2018-12-22
一、構建最優二叉樹
①、節點類:五個屬性:結點的資料、父結點、左子結點、右子結點、赫夫曼編碼
/** * 樹的結點類 * * @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()); // 遞迴
}
}