几个问题:
1,没有构造出真正的赫夫曼树,最后结果需要自己手动画出来;
2,针对于某一类中的set和get方法,每次都忘了用,最后才是反应过来强行加上的,说到底还是代码规范问题,同时也可以理解为封装特性,有个坏习惯,每次总是直接的去访问成员变量。
3,自动生成重写一个类的toString方法时,Ecplise会报以下错。查了查说是JDK9和Ecplise有冲突??
话不多说直接开始。
一:首先建立一个关于结点的类,用于维护该结点的相关信息。
class nodeinf {
/**
* nodeinf用来维护该节点的相关信息
* @param name名字
* @param fre频率
* @param left左孩子名字
* @param right右孩子名字
*/
private String name;
private int fre;
private String left;
private String right;
public nodeinf(String name,int fre) {
this.name=name;
this.fre=fre;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getFre() {
return fre;
}
public void setFre(int fre) {
this.fre = fre;
}
public String getLeft() {
return left;
}
public void setLeft(String left) {
this.left = left;
}
public String getRight() {
return right;
}
public void setRight(String right) {
this.right = right;
}
@Override
public String toString() {
return "name="+this.name+"\tfre="+this.fre+"\tleft="+this.left+"\tright="+this.right;
}
}
其中的tostring方法是重写的,方便用于打印某一结点的信息。
二:再次想到huffman code其中蕴含着贪心算法的思想,贪心算法是怎样体现的呢。
每次按照频率升序排序,选择前两个结点进行组合。看似是单纯的对于结点的排序,但实际上是针对于nodeinf对象排序的。针对对象排序,我们可以想到利用比较器。这里我选择Comparator比较器(为什么我也不知道,比较器这部分后面还是需要再看看)。
/**
* 利用比较器实现按fre频率升序排序
* @author Oliver
*
*/
class mycomparator implements Comparator<nodeinf>{
@Override
public int compare(nodeinf arg0, nodeinf arg1) {
// TODO Auto-generated method stub
return arg0.getFre()-arg1.getFre();
}
}
三:huffman编码的核心部分。
一次循环:首次排序后,将下标为0、1的元素整合,整合之后新结点为父节点,父节点的频率为两者相加,同时进行赋值操作以维护当前父节点的左右孩子了。然后再进行下一个循环。
如何实现上述过程,不妨用一个ArrayList来保存所有结点。0、1元素整合之后再删除,然后将新的父节点加入进去到这个ArrayList中去,再进行排序。直到我们的集合长度为1时,表示根节点已经形成。下面的最后一张图也就是集合长度为1的情况。
每次我都用左右两个孩子的名字相加得到父节点的名字,这样会更加直观的看到其中过程。
代码:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/**
* 赫夫曼编码的实现
* @author Oliver
*
*/
public class Huffman_Implement {
public static void main(String[] args) {
ArrayList<nodeinf> list=new ArrayList<>();
Huffman_Implement test=new Huffman_Implement();
nodeinf node1=new nodeinf("a",45);
nodeinf node2=new nodeinf("b",13);
nodeinf node3=new nodeinf("c",12);
nodeinf node4=new nodeinf("d",16);
nodeinf node5=new nodeinf("e",9);
nodeinf node6=new nodeinf("f",5);
list.add(node1);
list.add(node2);
list.add(node3);
list.add(node4);
list.add(node5);
list.add(node6);
System.out.println("-----------------实现huffman code之前-----------------");
test.print(list);
System.out.println("---------------------最后根节点为:---------------------\n"+test.createTree(list));
}
/**
* 构造赫夫曼树
* @param list
* @return
*/
private nodeinf createTree(ArrayList<nodeinf> list) {
while(list.size()>1) {
sort(list);
nodeinf left=list.get(0);
nodeinf right=list.get(1);
//"+"操作符重载啦
nodeinf pre=new nodeinf(list.get(0).getName()+list.get(1).getName(),
list.get(0).getFre()+list.get(1).getFre());
pre.setLeft(left.getName());
pre.setRight(right.getName());
//因为第一个remove(0)执行后,可以理解为List立即将所有元素向前推一个,所以要再执行一次remove(0),而不是remove(1)
list.remove(0);
list.remove(0);
list.add(pre);
}
return list.get(0);
}
/**
* 排序方法
* @param list
*/
private void sort(ArrayList<nodeinf> list) {
Collections.sort(list, new mycomparator());
System.out.println("---------------------一次排序后---------------------");
print(list);
}
/**
* 打印方法
* @param list
*/
private void print(ArrayList<nodeinf> list) {
for(nodeinf node:list) {
System.out.println(node.toString());
}
}
}
执行结果:
其中可以从根节点中直接可以看到,名字的顺序就是从左到右的叶子结点。