2013.09.24
今天我们来重温一下以前学习的关于哈夫曼树的构建。
回顾一下,我们给定了一些节点,每个节点都有对应的数值,现在我们要用这些节点生成二叉树,而且要求加权路径最小,即权值乘以节点对应的数值,然后再求和,我们要求的是这个最小。比如我们现在有64个节点,要是按照平时,我们会将64个节点一字排开,然后两两生成父节点,然后父节点再生父节点,直至生成以二叉树。但是这样的话我们显然可以看出加权路径不是最优的,因为大数值点和小数值点在一个水平上。我们如果要实现加权路径最小,那么我们就更希望小数值节点在下面,而大数值节点在上面。哈夫曼树的构建正是基于这一原理,实现了对字符串的压缩,进而可以去实现对于文件的压缩。
在进行压缩前,我们先定义一个节点类:
public class Node {
Node left;//左节点
Node right;//右节点
char ch;//节点对应的字符
int num;//数值
}
还有就是编码类:
public class Code {
private char ch;// 字符
private int num;// 出现次数
private String str;// 对应编码
}
当然基础类还会有构造方法和get,set方法,我们这里就不一一阐述了。
然后我们就要来构建哈夫曼树:
首先我们定义两个队列:
ArrayList<Code> code = new ArrayList<Code>();
ArrayList<Node> node = new ArrayList<Node>();
code用于存放编码,node用于存放节点。
下面我们就要开始进行哈夫曼树的构建了:
1、我们要开始字符串的统计,并将字符和其出现次数生成编码存到编码队列里:
public void countString(String str) {
char[] ch = str.toCharArray();
for (int i = 0; i < ch.length; i++) {
boolean bol = true;
for (int j = 0; j < code.size(); j++) {
if (ch[i] == code.get(j).getCh()) {
code.get(j).setNum(code.get(j).getNum() + 1);
bol = false;
}
}
if (bol) {
code.add(new Code(ch[i]));
}
}
}
2、创建哈夫曼树:
public void creatTree() {
for (int i = 0; i < code.size(); i++) {
node.add(new Node(code.get(i).getCh(), code.get(i).getNum()));// 将叶子节点输入
}
sortNode();// 进行编码排序
while (node.size() > 1) {
Node temp = new Node(node.get(0), node.get(1), node.get(0).getNum()
+ node.get(1).getNum());
node.remove(0);
node.remove(0);
node.add(temp);
sortNode();
}
}
其中用到sortNode方法,是用来对节点对应的数值进行一个排序:
private void sortNode() {
for (int i = 0; i < node.size(); i++) {
for (int j = i; j < node.size(); j++) {
if (node.get(i).getNum() > node.get(j).getNum()) {
Node node1 = node.get(i);
Node node2 = node.get(j);
node.set(i, node2);
node.set(j, node1);
}
}
}
}
3、根据节点生成码表:
public void creatCodeList() {
String str = new String();
codeList(node.get(0), str);
for (int i = 0; i < code.size(); i++) {
System.out.println(code.get(i).getCh() + " "
+ code.get(i).getStr());
}
}
这里我们用递归来生成码表,写了codeList方法:
private void codeList(Node node, String str) {
if (node.left == null && node.right == null) {
for (int i = 0; i < code.size(); i++) {
if (code.get(i).getCh() == node.getCh()) {
code.get(i).setStr(str);
}
}
}
if (node.left != null) {
codeList(node.left, str + "0");
}
if (node.right != null) {
codeList(node.right, str + "1");
}
}
4、根据码表生成最终编码:
public String printCodeList(String s) {
char[] ch = s.toCharArray();
String str = new String();
for (int i = 0; i < ch.length; i++) {
for (int k = 0; k < code.size(); k++) {
if (code.get(k).getCh() == ch[i]) {
str += code.get(k).getStr();
break;
}
}
}
System.out.println(str);
return str;
}
5、根据码表和生成的编码还原编码:
public String restoreCodeList(String codeStr) {
String str = new String();
for (int i = 0; i < codeStr.length(); i++) {
for (int j = i; j <= codeStr.length(); j++) {
String temp = codeStr.substring(i, j);
for (int k = 0; k < code.size(); k++) {
if (code.get(k).getStr().equals(temp)) {
str += code.get(k).getCh();
i = j;
}
}
}
}
System.out.println(str);
return str;
}
这样,哈夫曼树的构建就完成了,下一次我们就会讲如何利用哈夫曼树实现文件的压缩,敬请期待~