原理:哈夫曼編碼是根據將已給出的權值作為葉子結點,生成一顆哈夫曼樹,然后使得權重最小。
首先生成已給權重的所有的葉子結點,然后取所有節點中最小和次小的結點作為左右孩子生成一個哈夫曼樹,計算出父節點的權重放入給出的權重森林中,並把之前的最小和次小的結點從森林中刪除,再在種種森林中找最小和次小的結點生成權重樹....直到最終只剩下一個樹為止。
哈夫曼樹的結點用如下結點表示:
(有權重,左右孩子,父節點,然后設置一個標識符標志結點是否已經放入哈夫曼樹)
package tree;
/**********哈夫曼樹的節點類描述*********/
public class HuffmanNode {
private int weight;//權重
private int flag;//是否加入到huffman樹中
private HuffmanNode parent,lchild,rchild;//樹種的結點之間的關系
public HuffmanNode(){//構造空結點
this(0);
}
public HuffmanNode(int weight){//構造只具有權值的空結點
this.weight=weight;
flag=0;
parent=lchild=rchild=null;
}
public void setweight(int weight){
this.weight=weight;
}
public int getweight(){
return weight;
}
public void setflag(int flag){
this.flag=flag;
}
public int getflag(){
return flag;
}
public void setparent(HuffmanNode parent){
this.parent=parent;
}
public HuffmanNode getparent(){
return parent;
}
public void setlchild(HuffmanNode lchild){
this.lchild=lchild;
}
public HuffmanNode getlchild(){
return lchild;
}
public void setrchild(HuffmanNode rchild){
this.rchild=rchild;
}
public HuffmanNode getrchild(){
return rchild;
}
}
哈夫曼樹的構造:
用給出的權重數組構造哈夫曼樹,首先要先給每個權重生成哈夫曼結點代用(標志為放入哈夫曼樹 falg=0);
計算得出最終哈夫曼樹中會有多少個結點,由葉子結點為n,則總結點為2*n-1個;
所以要其次接入n-1個結點(下標為:n--m-1);首先選n個中的二最小和次小結點比較(設置一個比較函數對HN中的所有節點遍歷,找到之后設置flag=1,然后再次尋找HN中的最小結點),最終構成整個哈夫曼樹
然后由葉子結點開始讀出哈夫曼編碼:
package tree;
public class HuffmanTree {
public int[][] huffmanCoding(int[] w){
int n=w.length;
int m=2*n-1;
HuffmanNode[] HN=new HuffmanNode[m];//生成一個元素為哈夫曼樹結點的數組
int i;
for(i=0;i<n;i++){
HN[i]=new HuffmanNode(w[i]);//生成哈夫曼樹的葉子結點
}
for(i=n;i<m;i++){
HuffmanNode min1=selectMin(HN,i-1);
min1.setflag(1);
HuffmanNode min2=selectMin(HN,i-1);//上面找到的那個flag已標志為1,所以不會再次尋找
min2.setflag(1);//找到所有節點中權重最小的結點加入到哈夫曼樹中
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];//分配n個字符編碼存儲空間
for(int j=0;j<n;j++){//數組
int start=n-1;
//從后開始 對HuffCode數組每個結點的下標由后向前填充,保證編碼的前綴相同00... huffCode[0][6]=1 huffCode[0][5]=0
//則huffCode 為0000 0001即小標為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;
}//對同一個結點的路徑做遍歷
HuffCode[j][start--]=-1;//對剩余的路徑為填充-1
}//對n個葉子結點的路徑做遍歷
return HuffCode;//返回哈夫曼編碼數組
}
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++){//n個葉子結點
System.out.print(w[i]+": ");
for(int j=0;j<HN[i].length;j++){//每個葉子結點對應一個結點的n個編碼位置做遍歷
if(HN[i][j]==-1){//值輸出有路徑的編碼位(n位中的后幾位)
for(int k=j+1;k<HN[i].length;k++)
System.out.print(HN[i][k]);
break;
}
}
System.out.println();
}
}
}
結果:
哈弗滿編碼為:
23: 01
11: 001
5: 11111
3: 11110
29: 10
14: 110
7: 1110
8: 000
哈夫曼樹為:

