1. 程式人生 > >哈夫曼樹(Huffman Tree) 實現

哈夫曼樹(Huffman Tree) 實現

假設有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