原理:哈夫曼编码是根据将已给出的权值作为叶子结点,生成一颗哈夫曼树,然后使得权重最小。
首先生成已给权重的所有的叶子结点,然后取所有节点中最小和次小的结点作为左右孩子生成一个哈夫曼树,计算出父节点的权重放入给出的权重森林中,并把之前的最小和次小的结点从森林中删除,再在种种森林中找最小和次小的结点生成权重树....直到最终只剩下一个树为止。
哈夫曼树的结点用如下结点表示:
(有权重,左右孩子,父节点,然后设置一个标识符标志结点是否已经放入哈夫曼树)
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
哈夫曼树为: