哈夫曼樹(Huffman Tree) 實現
阿新 • • 發佈:2019-02-19
假設有n個權值,則構造出的哈夫曼樹有n個葉子結點。 n個權值分別設為 w1、w2、…、wn,則哈夫曼樹的構造規則為:
(1) 將w1、w2、…,wn看成是有n 棵樹的森林(每棵樹僅有一個結點); (2) 在森林中選出兩個根結點的權值最小的樹合併,作為一棵新樹的左、右子樹,且新樹的根結點權值為其左、右子樹根結點權值之和; (3)從森林中刪除選取的兩棵樹,並將新樹加入森林; (4)重複(2)、(3)步,直到森林中只剩一棵樹為止,該樹即為所求得的哈夫曼樹。思路:
使用二叉樹作為輸出資料結構
class HNode{ int value; HNode lChild; HNode rChild; }
用所有的輸入節點建立一個小根堆。兩次出堆,兩個最小的元素, 將其合併為新的二叉樹。然後入堆。直到堆中只有一個元素。
改元素為Huffman樹的根. 我還沒用寫用字元拼湊樹的畫面的程式。 暫時用後續遍歷和層次遍歷輸出我們的huffman樹吧。
這個程式其實寫得不是很好。 小根堆和Huffman樹的演算法 高度耦合。難以重構,我為了圖方便亂寫。
如果以後有時間,我會系統寫一套容器,高度可重構,實現經典資料結構,作為工具使用。
好的程式設計習慣,應該寫一個泛型的公共堆,一個二叉樹結構及其API, 然後實現一個孤立的構建Huffman樹方法,以一個節點集合為輸入,一棵二叉樹為輸出。
還是老調子, 二叉樹的儲存方法有多種, 有常用的有二叉連結串列,三叉連結串列,順序表,父矩陣等
但是無論那種儲存解構,他們都有一套公共的API. Huffman tree演算法依賴於輸入集合 , 堆的API 而非具體儲存結構。
package lee.tree; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.Scanner; import lee.tools.CircleQueue; import lee.tools.Queue; public class HuffmanTree { class HNode{ int value; HNode lChild; HNode rChild; } HNode[] arr; int leafCount; int end; public HuffmanTree(String path) throws FileNotFoundException { init(path); } public void init(String path) throws FileNotFoundException { Scanner in = new Scanner(new FileInputStream(path)); if (in.hasNext()) { leafCount = in.nextInt(); } else { leafCount = 0; } int i = 0; arr = new HNode[2 * leafCount - 1]; System.out.println("leafCount=" + leafCount); while (in.hasNext()) { HNode node = new HNode(); node.value = in.nextInt(); arr[i] = node; i++; } } public void buildHuffmanTree() { end = 0; for (int i = 1; i < leafCount; i++) { insert(arr[i]); } while (end > 0) { HNode min1 = getTop(); HNode min2 = getTop(); HNode node = new HNode(); node.lChild = min1; node.rChild = min2; node.value = min1.value + min2.value; min1.father = node; min2.father = node; insert(node); } HNode root = getTop(); printLevel(root); } //---------------------遍歷二叉樹 輸出----------------------------------------- public void printLastOrder(HNode root) { if (root != null) { printLastOrder(root.lChild); printLastOrder(root.rChild); System.out.print(" " + root.value); } } public void preOrder(HNode root) { if (root != null) { System.out.print(" " + root.value); preOrder(root.lChild); preOrder(root.rChild); } } public void midOrder(HNode root) { if (root != null) { System.out.print(" " + root.value); midOrder(root.lChild); midOrder(root.rChild); } } //---------------------------------------------------- private void siftUp(int i) { for (; i > 0; i = (i - 1) / 2) { int father = (i - 1) / 2; if (arr[father].value > arr[i].value) { HNode temp = arr[father]; arr[father] = arr[i]; arr[i] = temp; } else { break; } } } private void siftDown(int i) { for (i = i * 2 + 1; i <= end; i = i * 2 + 1) { if (arr[i].value > arr[i + 1].value) { i++; } int father = (i - 1) / 2; if (arr[father].value > arr[i].value) { HNode temp = arr[father]; arr[father] = arr[i]; arr[i] = temp; } else { break; } }// for } public void insert(HNode node) { arr[++end] = node; siftUp(end); } public HNode getTop() { HNode top = arr[0]; arr[0] = arr[end--]; siftDown(0); return top; } }
程式輸入輸出:
public static void main(String[] args) throws IOException{
HuffmanTree hf = new HuffmanTree("D:\\huffman.txt");
hf.buildHuffmanTree();
}
第一行為輸入節點數
第二行為 輸入節點集
測試案例:
4
1 2 3 4 5 6 7
前序遍歷輸出結果
leafCount=7
28 12 6 3 3 1 2 6 16 7 9 4 5
中序遍歷輸出結果:
leafCount=7
28 12 6 3 3 1 2 6 16 7 9 4 5
後序遍歷輸出結果:leafCount=7
3 1 2 3 6 6 12 7 4 5 9 16 28