一、构建最优二叉树
①、节点类:五个属性:结点的数据、父结点、左子结点、右子结点、赫夫曼编码
/**
* 树的结点类
*
* @author lenovo
*
*/
public class TreeNode {
private Object obj; // 数据
private TreeNode parent; // 父结点
private TreeNode leftchild; // 左子结点
private TreeNode rightchild; // 右子结点
private String code; //赫夫曼编码
/**
* 重写构造方法
* @param obj
*/
public TreeNode(Object obj){
this.obj=obj;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public TreeNode getParent() {
return parent;
}
public void setParent(TreeNode parent) {
this.parent = parent;
}
public TreeNode getLeftchild() {
return leftchild;
}
public void setLeftchild(TreeNode leftchild) {
this.leftchild = leftchild;
}
public TreeNode getRightchild() {
return rightchild;
}
public void setRightchild(TreeNode rightchild) {
this.rightchild = rightchild;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
②、构建最有二叉树的思路:最优二叉树即整棵树的总权值最小,只有每个叶结点才存放数据。先建立一个队列(优先队列,已经排好序),根据数组中的数据建立结点存放到队列中。然后对队列进行构树操作,每次从优先队列中取出结点(用poll方法),分别设为左子结点、右子结点,再将左子结点和右子结点的数据相加并为父结点,再把父结点放到优先队列中去,再取出两个结点,分别设为左子结点、右子结点,再将左子结点和右子结点的数据相加并为父结点,如此循环,去两个放一个,直到优先队列中的只剩下一个结点,再将最后一个结点取出,设为最终的根结点。
/**
* 实现比较器的类
* @author lenovo
*
*/
public class MyComparator implements Comparator<TreeNode>{
/**
* 比较TreeNode中两个obj的大小,o1.getObj()>o2.getObj()返回1(从大到小排序),小于返回-1(从小到大排序)
*/
@Override
public int compare(TreeNode o1, TreeNode o2) {
return (Integer)o1.getObj()-(Integer)o2.getObj();
}
}
/**
* 将数组转化为最优二叉树的方法
*
* @param array
* @return:最优二叉树的根节点
*/
public TreeNode arraytoTree(int[] array) {
if (array == null) {
throw new RuntimeException("数组为空");
}
// 定义优先队列一个队列,指定初始长度和排序方式
java.util.PriorityQueue<TreeNode> pqueue = new java.util.PriorityQueue<TreeNode>(
array.length, new MyComparator());
// 遍历数组,得到数组中的元素,创建TreeNode对象,加入到优先队列中去
for (int i = 0; i < array.length; i++) {
TreeNode tree = new TreeNode(array[i]);
pqueue.add(tree);
}
// 如果优先队列中还有元素,构建赫夫曼树
while (pqueue.size() > 1) {
TreeNode ltree = pqueue.poll(); // 得到左子结点,poll()方法,取出列表头并移除
TreeNode rtree = pqueue.poll(); // 得到右子结点
// 创建根节点
TreeNode root = new TreeNode((Integer) ltree.getObj()
+ (Integer) rtree.getObj());
// 设置关系
root.setLeftchild(ltree);
root.setRightchild(rtree);
ltree.setParent(root);
rtree.setParent(root);
// 加入子结点
pqueue.add(root);
}
//得到根结点
TreeNode root = pqueue.peek();
return root;
}
二、对叶节点进行赫夫曼编码
从根结点开始,在父结点左边的编码为0,在父结点右边的编码为1,直到叶节点,即可以的得到每个叶节点的赫夫曼编码。
/**
* 设置并打印叶结点的赫夫曼编码 左:0; 右:1
*
* @param root
* :根结点
*/
public void printCode(TreeNode root) {
// 如果为叶节点
if (root.getLeftchild() == null && root.getRightchild() == null) {
System.out.println("结点:" + root.getObj());
System.out.println("\t\t赫夫曼编码:"+ root.getCode());
}
// 左子结点为0
if (root.getLeftchild() != null) {
String s = root.getCode();
if(s==null){ //如果是根结点的子左结点
root.getLeftchild().setCode("0");
}else{
root.getLeftchild().setCode(s+'0');
}
printCode(root.getLeftchild()); // 递归
}
// 右子结点为1
if (root.getRightchild() != null) {
String s = root.getCode();
if (s ==null) { //如果是根结点的子右结点
root.getRightchild().setCode( "1");
}else{
root.getRightchild().setCode( s+'1');
}
printCode(root.getRightchild()); // 递归
}
}