1、基本概念
哈夫曼树又称最优树(二叉树),是一类带权值路径最短的树。构造这种树的算法是最早由哈夫曼(Huffman)于1952年提出的,这种树在信息检索中很有用。
①节点之间的路径长度:从一个节点到另一个节点之间的分支数目;
②树的路径长度:从树的根节点中每一个节点的路径长度之和。
2、哈夫曼树编码规则:
从哈夫曼树根节点开始,对左子树分配代码“0”,右子树分配代码“1”,一直到达叶节点为止,然后将树根每条路径到达叶子节点的代码排列起来,便得到了哈夫曼编码。
例:对于字符串EMCAD编码。若采用等长编码,则:
E M C A D
000 001 010 011 100
EMCAD=>000001010011100,一共十五位
设各字母出现的频率为{E,M,C,A,D} = {1,2,3,3,4}.以频率为权值生成哈夫曼树,并在叶子上标注对应的字母,树枝分配代码“0”或者“1”:
由此哈夫曼编码树获得每个字母编码:
E M C A D
000 001 01 10 11
这样大大地减少了编码的位数
3、哈夫曼树的完整代码实现:
首先定义节点:
public class Node {
public Node left;//左子节点
public Node right;//右子节点
String str;//字符
String code;//哈夫曼编码
int weight;//权值
}
然后根据权值建树,返回根节点,我们将其写在createHFM这个函数中:
public Node createHFM(String[] strs, int[] weights) {
Node[] nodes = new Node[strs.length];
for (int i = 0; i < nodes.length; i++) {
nodes[i] = new Node();
nodes[i].weight = weights[i];
nodes[i].str = strs[i];
}
while (nodes.length > 1) {
sort(nodes);
Node n1 = nodes[0];
Node n2 = nodes[1];
Node node = new Node();
node.left = n1;
node.right = n2;
node.weight = n1.weight + n2.weight;
Node[] nodes2 = new Node[nodes.length - 1];
for (int i = 2; i < nodes.length; i++) {
nodes2[i - 2] = nodes[i];
}
nodes2[nodes2.length - 1] = node;
nodes = nodes2;
}
return nodes[0];
}
①先将字符数组和其对应的权值转换为Node数组;(2-7行)
②在while循环中建树,直到只剩下一个节点;(8行)
③先将节点以权值递增的顺序排序;(9行)
④建立一个新节点,将第一个节点(权值最小的节点)设为该节点的左子节点,将第二个节点(权值第二小的节点)设为该节点的右子节点,该节点的权值等于两个节点的权值之和;(10-15行)
⑤将两个权值最小的节点删除,把新生成的节点加入数组中,重复上述步骤,直到队列中只剩下一个节点。(16-21行)
排序算法:这里我使用了冒泡排序:
public void sort(Node[] nodes) {
for (int i = 0; i < nodes.length; i++) {
for (int j = i + 1; j < nodes.length; j++) {
if (nodes[j].weight < nodes[i].weight) {
Node n = new Node();
n = nodes[j];
nodes[j] = nodes[i];
nodes[i] = n;
}
}
}
}
print函数打印哈夫曼树编码:
public void print(Node node, String code) {
//递归的方法
if (node == null) {
return;
}
else{
if (node.left == null && node.right == null) {//该节点是叶子节点
String msg = node.str + "权值:" + node.weight + " HFM编码:" + code;
System.out.println(msg);//打印相关信息
}
print(node.left, code + "0");//左子节点的编码后加0
print(node.right, code + "1");//右子节点的编码后加1
}
}
测试代码运行结果:
public static void main(String[] args) {
try {
//String = "fasdfasfqkejhkqwnfs,zjalfuoiurjq,a..fasdfa.123efafzcvs";
HFM hfm = new HFM();
String[] strs = new String[]{"A", "B", "C", "D", "E", "F"};
int[] weights = new int[]{4, 6, 1, 9, 8, 2};
Node root = hfm.createHFM(strs, weights);
hfm.print(root, "");
} catch (Exception e) {
e.printStackTrace();
}
}
完整代码:
Node类:
package p1;
public class Node {
public Node left;//左子节点
public Node right;//右子节点
String str;//字符
String code;//哈夫曼编码
int weight;//权值
}
HFM类:
package p1;
import javax.swing.*;
import javax.xml.crypto.NodeSetData;
public class HFM {
public Node createHFM(String[] strs, int[] weights) {
Node[] nodes = new Node[strs.length];
for (int i = 0; i < nodes.length; i++) {
nodes[i] = new Node();
nodes[i].weight = weights[i];
nodes[i].str = strs[i];
}
while (nodes.length > 1) {
sort(nodes);
Node n1 = nodes[0];
Node n2 = nodes[1];
Node node = new Node();
node.left = n1;
node.right = n2;
node.weight = n1.weight + n2.weight;
Node[] nodes2 = new Node[nodes.length - 1];
for (int i = 2; i < nodes.length; i++) {
nodes2[i - 2] = nodes[i];
}
nodes2[nodes2.length - 1] = node;
nodes = nodes2;
}
return nodes[0];
}
public void sort(Node[] nodes) {
for (int i = 0; i < nodes.length; i++) {
for (int j = i + 1; j < nodes.length; j++) {
if (nodes[j].weight < nodes[i].weight) {
Node n = new Node();
n = nodes[j];
nodes[j] = nodes[i];
nodes[i] = n;
}
}
}
}
public void print(Node node, String code) {
//递归的方法
if (node == null) {
return;
}
else{
if (node.left == null && node.right == null) {//该节点是叶子节点
String msg = node.str + "权值:" + node.weight + " HFM编码:" + code;
System.out.println(msg);//打印相关信息
}
print(node.left, code + "0");//左子节点的编码后加0
print(node.right, code + "1");//右子节点的编码后加1
}
}
public static void main(String[] args) {
try {
//String = "fasdfasfqkejhkqwnfs,zjalfuoiurjq,a..fasdfa.123efafzcvs";
HFM hfm = new HFM();
String[] strs = new String[]{"A", "B", "C", "D", "E", "F"};
int[] weights = new int[]{4, 6, 1, 9, 8, 2};
Node root = hfm.createHFM(strs, weights);
hfm.print(root, "");
} catch (Exception e) {
e.printStackTrace();
}
}
}