1.哈夫曼树简述
给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。
为什么使用哈夫曼编码压缩文件?
一个字符八个bits,若使用编码来表示字母,节省空间,传输速度快。根据字母出现的频率构建哈夫曼树,频数即为权值,频数出现最多的字母更靠近哈夫曼树的根节点,编码长度也更短。
2.构造树的节点
先构造一个哈夫曼树的节点类,节点类包含5个元素:指向左节点、右节点的指针、代表的字母、频数和对应编码。
private HaffNode left,right;
private char ch;
private int n;
private String code;
复写节点类的compareTo方法,方便排序。
//便于使用列表排序
@Override
public int compareTo(Object o) {
HaffNode a=(HaffNode)o;
if(this.n>a.n)
return 1;
else if(this.n==a.n)
return 0;
else
return -1;
}
3.构造哈夫曼树的类(压缩)
构造一个哈夫曼树类,类中有6个元素:包含26个字母和空格的字符数组、读取文件得到的字符数组、保存各个节点权值的整数数组、哈夫曼树的根节点,哈夫曼树的列表形式,以及保存各个节点编码值的字符串数组。
//创立字母表
private static char[] ch={'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',' '};
//文件内容的字符数组
private char[] a=new char[100];
//各个字符的频数
private int[] sum=new int[27];
//树的根节点
private HaffNode root;
//保存压缩文件的单个编码
private String[] strings=new String[28];
//哈夫曼树的列表形式
private LinkedList treelist=new LinkedList();
读取未压缩文件
//从文件中读取字节流,并保存为字符数组
public void read(String path,String name) throws IOException {
int i=0;
File f=new File(path,name);
Reader reader=new FileReader(f);
while(reader.ready()){
a[i++]=(char)reader.read();
}
reader.close();
System.out.print("压缩前的文件内容为:");
for(int k=0;k
System.out.print(a[k]);
}
System.out.println();
}
进行频数统计
//统计各个字母的频次
public void count(){
for(int k=0;k
for (int j=0;j
if(a[k]==ch[j])
sum[j]++;
}
}
}
构造一棵哈夫曼树,将所有节点都加入列表后先排序一次。当列表长度大于1时,每次选取两个列表中权值最小的节点(也就是列表第1位和第2位的节点),构成一个字符为空的父节点,列表中删除这两个节点,把父节点加入列表后,对列表再次排序,循环到列表中只剩下一个节点时,该节点为根节点,保存。
//构造哈夫曼树,保存树的根节点
public LinkedList createTree() {
count();
//匹配频数和字符构成节点,全部加入树的列表
for (int i = 0; i < 27; i++) {
HaffNode node = new HaffNode();
node.setCh(ch[i]);
node.setN(sum[i]);
treelist.add(i, node);
}
Collections.sort(treelist);
while (treelist.size() > 1) {
//获得两个权值最小的节点
HaffNode first = (HaffNode) treelist.removeFirst();
HaffNode second = (HaffNode) treelist.removeFirst();
//将这两个权值最小的节点构造成父节点
HaffNode parent = new HaffNode();
parent.setN(first.getN() + second.getN());
parent.setLeft(first);
parent.setRight(second);
//把父节点添加进列表,并重新排序
treelist.add(parent);
Collections.sort(treelist);
}
root= (HaffNode) treelist.getFirst();
}
根据哈夫曼树获取编码:左子树编码为0,右子树编码为1