1. 程式人生 > >資料結構 JAVA描述(五)哈夫曼樹,樹與森林

資料結構 JAVA描述(五)哈夫曼樹,樹與森林

相關概念:

  • 結點的帶權路徑長度: 該結點的路徑長度 × 該結點的權值

  • 最優二叉樹(哈夫曼樹):給定n個權值並作為n個葉結點按一定規則構造的一棵二叉樹,使其帶權路徑長度達到最小值,則這棵二叉樹被稱為最優二叉樹。

  • 字首編碼:在所有字元的編碼中,任何一個字元都不是另一個字元的字首。

package BiTree;

/**
 * @description 哈夫曼樹結點結構
 *  
 * @date 2015年12月30日
 */
class HuffmanNode {
    private int weight; // 結點權值
    private int flag; // 結點是否加入哈夫曼樹標誌
private HuffmanNode parent, lchild, rchild; // 父結點,左右孩子結點 public HuffmanNode(){ this(0); } // 構造一個具有權值的結點 public HuffmanNode(int weight){ this.weight = weight; flag = 0; parent = lchild = rchild = null; } public int getWeight() { return
weight; } public void setWeight(int weight) { this.weight = weight; } public int getFlag() { return flag; } public void setFlag(int flag) { this.flag = flag; } public HuffmanNode getParent() { return parent; } public void setParent
(HuffmanNode parent) { this.parent = parent; } public HuffmanNode getLchild() { return lchild; } public void setLchild(HuffmanNode lchild) { this.lchild = lchild; } public HuffmanNode getRchild() { return rchild; } public void setRchild(HuffmanNode rchild) { this.rchild = rchild; } } /** * @description 構造哈夫曼樹和哈夫曼編碼 * * @date 2015年12月30日 */ public class HuffmanTree { // W存放n個具有權值的結點 public int[][] huffmanCoding(int[] W){ int n = W.length; //字元個數 int m = 2 * n - 1; //哈夫曼樹的結點 HuffmanNode[] HN = new HuffmanNode[m]; for(int i = 0; i < n; i++){ HN[i] = new HuffmanNode(W[i]); } //建立哈夫曼樹 for(int i = n; i < m; i++){ // 在HN[]中選擇不再哈夫曼樹weight最小的兩個結點min1和min2 HuffmanNode min1 = selectMin(HN, i-1); min1.setFlag(1); HuffmanNode min2 = selectMin(HN, i-1); min2.setFlag(1); // 構造min1和min2的父結點,並修改父結點的權值 HN[i] = new HuffmanNode(); min1.setParent(HN[i]); min2.setParent(HN[i]); HN[i].setLchild(min1); HN[i].setRchild(min2); HN[i].setWeight(min1.getWeight() + min2.getWeight()); } // 從葉子結點到根 逆向求每個字元的哈夫曼編碼 int[][] HuffCode = new int[n][n]; for(int j = 0; j < n; j++){ int start = n - 1; // 從葉子到根逆向求編碼 (儲存時倒序存,輸出時順序輸,-1為開始標誌 ) for(HuffmanNode c = HN[j], p = c.getParent(); p != null; c=p, p = p.getParent()){ if(p.getLchild().equals(c)){ HuffCode[j][start--] = 0; } else{ HuffCode[j][start--] = 1; } // 編碼開始標誌是-1 HuffCode[j][start] = -1; } } return HuffCode; } /** * @description 在HN[0……i-1]選擇不在哈夫曼樹中且weight最小的結點 * @param HN * @param end * @return * @author liuquan * @date 2015年12月30日 */ private HuffmanNode selectMin(HuffmanNode[] HN, int end) { HuffmanNode min = HN[end]; //先假設最後一個元素為最小值 for(int i = 0; i < end; i++){ HuffmanNode h = HN[i]; // 不在哈夫曼樹中且權值最小的結點 if(h.getFlag() == 0 && h.getWeight() < min.getWeight()){ min = h; } } return min; } public static void main(String[] args) { int[] W = {23, 11, 5, 3, 29, 14, 7, 8}; HuffmanTree T = new HuffmanTree(); //求哈夫曼編碼 int[][] HN = T.huffmanCoding(W); System.out.println("哈夫曼編碼為:"); for(int i = 0; i < HN.length; i++){ System.out.print("權重:" + W[i] + " "); for(int j = 0; j < HN[i].length; j++){ if(HN[i][j] == -1){ for(int k = j + 1; k < HN[i].length; k++){ System.out.print(HN[i][k]); } break; } } System.out.println(); } } }
  1. 樹轉換成二叉樹:

    • 加線:將樹中所有兄弟之間加一條連線

    • 刪線:對於每一個結點,只保留它與第一個孩子的結點間的連線,刪去它與其他孩子結點的連線

    • 旋轉:以根為軸,轉成二叉樹形式

  2. 二叉樹轉換成樹:

    • 加線:若某結點是父結點的左孩子,則將它的右分支向下的所有結點與它的父結點連線

    • 刪線: 將樹中所有雙親結點與右孩子結點的連線刪除

    • 旋轉:……

  3. 森林與二叉樹的轉換

    • 將每一棵樹轉換成相應的二叉樹

    • 按照森林中樹的先後順序,將後一棵樹視為前一棵二叉樹的右子樹依次連線.

結點包含 資料域資訊data + 父結點在儲存結構中的位置資訊parent。parent=-1代表其是根結點。

結點包含 資料域資訊data + 孩子連結串列的頭指標firstChild。

結點包含 資料域資訊data + 父結點在儲存結構中的位置資訊parent +孩子連結串列的頭指標firstChild。

結點包含 資料域資訊data + (左指標)孩子連結串列的頭指標firstChild + (右指標)該結點的下一個兄弟nextsibling