資料結構 JAVA描述(五)哈夫曼樹,樹與森林
阿新 • • 發佈:2019-02-09
相關概念:
結點的帶權路徑長度: 該結點的路徑長度 × 該結點的權值
最優二叉樹(哈夫曼樹):給定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();
}
}
}
樹轉換成二叉樹:
加線:將樹中所有兄弟之間加一條連線
刪線:對於每一個結點,只保留它與第一個孩子的結點間的連線,刪去它與其他孩子結點的連線
旋轉:以根為軸,轉成二叉樹形式
二叉樹轉換成樹:
加線:若某結點是父結點的左孩子,則將它的右分支向下的所有結點與它的父結點連線
刪線: 將樹中所有雙親結點與右孩子結點的連線刪除
旋轉:……
森林與二叉樹的轉換
將每一棵樹轉換成相應的二叉樹
按照森林中樹的先後順序,將後一棵樹視為前一棵二叉樹的右子樹依次連線.
結點包含 資料域資訊data + 父結點在儲存結構中的位置資訊parent。parent=-1代表其是根結點。
結點包含 資料域資訊data + 孩子連結串列的頭指標firstChild。
結點包含 資料域資訊data + 父結點在儲存結構中的位置資訊parent +孩子連結串列的頭指標firstChild。
結點包含 資料域資訊data + (左指標)孩子連結串列的頭指標firstChild + (右指標)該結點的下一個兄弟nextsibling