哈夫曼树小结

所谓哈夫曼树,又称为最优二叉树,要了解哈夫曼树,首先先来了解几个概念。树中一个结点到另一个结点之间的分支构成这两个结点之间的路径,路径上分支数目称为路径长度。树的长度是指从根结点到每一个叶子结点的路径长度之和。结点的带权路径长度为从从该结点到根结点之间的路径长度与结点上的权值的乘积。哈夫曼树就是带权路径长度最小的二叉树。现在我们可以根据给的任意一个整型数组构造一颗哈夫曼树,构造的思路是将数组中的每一个元素构造成结点对象,然后把每一个结点当成一棵树,将所有结点当成一片森林。然后每次从所有结点中取两个最小的结点,比较小的作为左子结点,比较大的作为右子结点,它们的和作为父结点构成一颗二叉树,然后把取出的这两个结点删除,将父结点放入剩下的结点中再重复以上过程直到最后只剩下一个结点,该结点即为哈夫曼树的根结点。从以上过程看,关键在于对数组的排序以及接收新的数组,这里用到java里面提供的优先队列PriorityQueue<E>,该队列的作用是将任意无序数组元素加入到该队列中,在该队列中这些元素会按某种顺序排好,只要按顺序取出就行了。查阅API文档可知,该队列定义了六个构造器,这里用到文档中的第四个构造器实例化一个该队列的对象,由该构造器的参数定义一个类来实现该参数接口类型,代码如下所示:
/**
* 实现比较对象类型接口类,该类作为优先队列的构造方法的参数传入
* @author lenovo
*
*/
class MyComparator implements Comparator<TreeNode>{

public int compare(TreeNode o1, TreeNode o2){

return (Integer)o1.getObj() - (Integer)o2.getObj();

}

public boolean equals(Object obj){
return false;
}

}

实现了该接口之后就可以实例化一个该队列的对象,然后将数组元素添加到该队列中,最后遍历队列,每次取出最小的两个元素,按上面思路就可以构造出哈夫曼树,具体代码如下所示:

/**
* 创建最优二叉树的方法
* @param arr:传入的数组
* @param TreeNode:返回根结点
*/
public TreeNode createHuffman(int[] arr){
//实例化一个优先队列对象
PriorityQueue<TreeNode> queue = new PriorityQueue<TreeNode>(11,new MyComparator());
TreeNode root=null;
//遍历数组,将数组元素构建成结点,并添加到队列中
for (int i=0;i<arr.length;i++){
//创建结点
TreeNode node = new TreeNode(arr[i]);
//将结点添加到队列中
queue.add(node);
}

//遍历队列,每次取出两个最小元素作为叶子结点,它们的和作为父结点建立二叉树,
//并从队列中删除这两个元素,同时把它们的父结点添加到队列中
while(queue.size()!=1){
//同时取两个最小元素
TreeNode node1 = queue.poll();
TreeNode node2 = queue.poll();

//根据取得的元素创建父结点
TreeNode fnode = new TreeNode((Integer)node1.getObj()+(Integer)node2.getObj());

//建立引用关系
if ((Integer)node1.getObj()<(Integer)node2.getObj()){//如果node1小于node2,则node1作为左子结点,node2作为右子结点
fnode.setLchild(node1);
fnode.setRchild(node2);
node1.setParent(fnode);
node2.setParent(node2);
}else {//如果node1大于于node2,则node1作为右子结点,node2作为左子结点
fnode.setLchild(node2);
fnode.setRchild(node1);
node1.setParent(fnode);
node2.setParent(node2);
}

//将父结点添加到队列中
queue.add(fnode);
root = fnode;

}
return root;
}
哈夫曼树生成之后,从根结点开始,对左子树分配代码“1”,右子树分配代码“0”,一直到达叶子结点为止,然后将从树根沿每条路径到达叶子结点的代码排列起来,就可以得到哈夫曼编码了,具体代码如下所示:
/**
* 根据根结点创建哈夫曼编码
* 规则:左子结点为:1,右子结点为:0
* @param root:根结点
*/
public void createCode(TreeNode root,String code){
//首先是确定各结点的路径
//得到子结点
TreeNode Lchild = root.getLchild();

TreeNode Rchild = root.getRchild();
//打印叶子节点
if (Lchild==null&&Rchild==null){
System.out.println(root.getObj()+"的编码为:"+code);
}

if (Lchild!=null){
//递归
createCode(Lchild,code+"1");
}
if (Rchild!=null){
//递归
createCode(Rchild,code+"0");
}

}
通过以下的主函数对上面的方法进行验证,主函数为:
/**
* 程序入口
* @param args
*/
public static void main(String args[]){
//实例化一个数组
int[] a = {8,6,3,4,9};
HuffmanTree ht = new HuffmanTree();
TreeNode root = ht.createHuffman(a);
ht.printTree(root);
System.out.println();
ht.createCode(root,"");

}
运行结果为:
30 13 6 7 3 4 17 8 9
6的编码为:11
3的编码为:101
4的编码为:100
8的编码为:01
9的编码为:00

通过检验可知结果正确,这就是自己对哈夫曼树的理解,如果有不正确的地方欢迎指正。

你可能感兴趣的:(哈夫曼编码,哈夫曼树,哈夫曼算法)