哈夫曼樹及一種java實現
阿新 • • 發佈:2019-02-19
最優二叉樹,也稱哈夫曼(Haffman)樹,是指對於一組帶有確定權值的葉結點,構造的具有最小帶權路徑長度的二叉樹。
那麼什麼是二叉樹的帶權路徑長度呢?
在前面我們介紹過路徑和結點的路徑長度的概念,而二叉樹的路徑長度則是指由根結點到所有葉結點的路徑長度之和。如果二叉樹中的葉結點都具有一定的權值,則可將這一概念加以推廣。設二叉樹具有n 個帶權值的葉結點,那麼從根結點到各個葉結點的路徑長度與相應結點權值的乘積之和叫做二叉樹的帶權路徑長度,記為:
其中Wk 為第k 個葉結點的權值,Lk 為第k 個葉結點的路徑長度。如圖6.16 所示的二叉樹,它的帶權路徑長度值WPL=2×2+4×2+5×2+3×2=28。
在給定一組具有確定權值的葉結點,可以構造出不同的帶權二叉樹。例如,給出4 個葉結點,設其權值分別為1,3,5,7,我們可以構造出形狀不同的多個二叉樹。這些形狀不同的二叉樹的帶權路徑長度將各不相同。
這五棵樹的帶權路徑長度分別為:
(a)WPL=1×2+3×2+5×2+7×2=32
(b)WPL=1×3+3×3+5×2+7×1=29
(c)WPL=1×2+3×3+5×3+7×1=33
(d)WPL=7×3+5×3+3×2+1×1=43
(1)由給定的n 個權值{W1,W2,…,Wn}構造n 棵只有一個葉結點的二叉樹,從而得到一個二叉樹的集合F={T1,T2,…,Tn};
(2)在F 中選取根結點的權值最小和次小的兩棵二叉樹作為左、右子樹構造一棵新的二叉樹,這棵新的二叉樹根結點的權值為其左、右子樹根結點權值之和;
(3)在集合F 中刪除作為左、右子樹的兩棵二叉樹,並將新建立的二叉樹加入到集合F 中;
(4)重複(2)(3)兩步,當F 中只剩下一棵二叉樹時,這棵二叉樹便是所要建立的哈夫曼樹。
圖6.18 給出了前面提到的葉結點權值集合為W={1,3,5,7}的哈夫曼樹的構造過程。可以計算出其帶權路徑長度為29,由此可見,對於同一組給定葉結點所構造的哈夫曼樹,樹的形狀可能不同,但帶權路徑長度值是相同的,一定是最小的。
那麼什麼是二叉樹的帶權路徑長度呢?
在前面我們介紹過路徑和結點的路徑長度的概念,而二叉樹的路徑長度則是指由根結點到所有葉結點的路徑長度之和。如果二叉樹中的葉結點都具有一定的權值,則可將這一概念加以推廣。設二叉樹具有n 個帶權值的葉結點,那麼從根結點到各個葉結點的路徑長度與相應結點權值的乘積之和叫做二叉樹的帶權路徑長度,記為:
其中Wk 為第k 個葉結點的權值,Lk 為第k 個葉結點的路徑長度。如圖6.16 所示的二叉樹,它的帶權路徑長度值WPL=2×2+4×2+5×2+3×2=28。
在給定一組具有確定權值的葉結點,可以構造出不同的帶權二叉樹。例如,給出4 個葉結點,設其權值分別為1,3,5,7,我們可以構造出形狀不同的多個二叉樹。這些形狀不同的二叉樹的帶權路徑長度將各不相同。
這五棵樹的帶權路徑長度分別為:
(a)WPL=1×2+3×2+5×2+7×2=32
(b)WPL=1×3+3×3+5×2+7×1=29
(c)WPL=1×2+3×3+5×3+7×1=33
(d)WPL=7×3+5×3+3×2+1×1=43
(e)WPL=7×1+5×2+3×3+1×3=29
由此可見,由相同權值的一組葉子結點所構成的二叉樹有不同的形態和不同的帶權路徑長度,那麼如何找到帶權路徑長度最小的二叉樹(即哈夫曼樹)呢?根據哈夫曼樹的定義,一棵二叉樹要使其WPL 值最小,必須使權值越大的葉結點越靠近根結點,而權值越小的葉結點越遠離根結點。哈夫曼(Haffman)依據這一特點提出了一種方法,這種方法的基本思想是:(1)由給定的n 個權值{W1,W2,…,Wn}構造n 棵只有一個葉結點的二叉樹,從而得到一個二叉樹的集合F={T1,T2,…,Tn};
(2)在F 中選取根結點的權值最小和次小的兩棵二叉樹作為左、右子樹構造一棵新的二叉樹,這棵新的二叉樹根結點的權值為其左、右子樹根結點權值之和;
(3)在集合F 中刪除作為左、右子樹的兩棵二叉樹,並將新建立的二叉樹加入到集合F 中;
(4)重複(2)(3)兩步,當F 中只剩下一棵二叉樹時,這棵二叉樹便是所要建立的哈夫曼樹。
圖6.18 給出了前面提到的葉結點權值集合為W={1,3,5,7}的哈夫曼樹的構造過程。可以計算出其帶權路徑長度為29,由此可見,對於同一組給定葉結點所構造的哈夫曼樹,樹的形狀可能不同,但帶權路徑長度值是相同的,一定是最小的。
下面是參考word2vec的中的程式碼對上面的{1,3,5,7}實現的哈夫曼樹。個人覺得word2vec裡面的哈夫曼樹實現程式碼寫的特別好:
package aturbo.huffman;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.TreeSet;
public class HuffmanTree {
public static void make(Collection<? extends HuffmanNode> nodes){
TreeSet<HuffmanNode> tree = new TreeSet<HuffmanNode>(nodes);
while (tree.size() > 1){
HuffmanNode left = tree.pollFirst();
HuffmanNode right = tree.pollFirst();
HuffmanNode parent = left.merge(right);
tree.add(parent);
}
}
public static void main(String[] args){
HuffmanNode h1 = new HuffmanNode(1);
HuffmanNode h3 = new HuffmanNode(3);
HuffmanNode h5 = new HuffmanNode(5);
HuffmanNode h7 = new HuffmanNode(7);
List<HuffmanNode> nodes = new ArrayList<HuffmanNode>();
nodes.add(h1);
nodes.add(h3);
nodes.add(h5);
nodes.add(h7);
HuffmanTree.make(nodes);
}
}
package aturbo.huffman;
public class HuffmanNode implements Comparable<HuffmanNode>{
protected int frequency = 0;
protected HuffmanNode parentnode;
protected int code = 0;
public int getFrequency() {
return frequency;
}
public void setFrequency(int frequency) {
this.frequency = frequency;
}
public HuffmanNode getParentnode() {
return parentnode;
}
public void setParentnode(HuffmanNode parentnode) {
this.parentnode = parentnode;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public HuffmanNode merge(HuffmanNode right){
HuffmanNode parent = new HuffmanNode(frequency+right.getFrequency());
parentnode = parent;
this.code = 0;
right.setParentnode(parent);
right.setCode(1);
return parent;
}
public HuffmanNode(int freq){
frequency = freq;
}
@Override
public int compareTo(HuffmanNode hn) {
if (frequency > hn.getFrequency()){
return 1;
} else {
return -1;
}
}
}
摘自:http://c.biancheng.net/cpp/html/984.html
參考:word2vec java版:https://github.com/siegfang/word2vec.git