输入:一个文本文件
输出:压缩后的文件
算法过程:
(1)统计文本文件中每个字符的使用频度
(2)创建哈夫曼树、构造huffman编码
(3)以二进制流形式压缩文件
其中的遇到的问题:
(1)哈夫曼树的总结点数为什么是(叶子结点个数*2-1)?
解:设度为0的结点个数为n0,度为1的结点个数为n1,度为2的结点个数为n2
总的结点个数=n0+n1+n2;
从度的角度:总的结点个数等于总度数+1,因为每个结点都连着一个度,只有根结点没有
总的结点个数=n1*1+n2*2+1
所以:n0+n1+n2=n1*1+n2*2+1 ——> n0=n2+1——>n2=n0-1
其中n0为叶子结点个数,所以,总的个数=n0+n0-1=n0*2-1
(2)压缩之后是把原来的字符替换成0,1,减少存储空间了吗?
解:这里只是模拟,用int类型存储0,1肯定比原来的字符占用的空间还大,实际上的压缩,0和1只占一个比特位,所以实际上是压缩了,这里只是模拟
(3)HaffCode和CodeNode的区别:
前者单纯作为树节点,通过各指针属性构造树,个数为(2*n-1)
后者着重存储节点编码,其中包含了树节点的一些属性信息,该类型的集合是一套编码方案,个数为n个
主要的过程:
1.统计文本内容的字符种类和出现的频率,用频率作为权重
2.初始化哈夫曼树节点,前n个元素存储叶子结点,后面存储非叶子节点,,flag都设为0,表示没有添加到树中
3.构建哈夫曼树。从没有加进树中的叶子结点和生成的非叶子节点中寻找最小和次小合并,将最小和次小放入树中,合并的父节点放到查询的范围中
4.生成哈夫曼编码。自下向上生成,一开始的start指向最后,然后依次前移。注意,start前移的次数多了依次,记得真正设置的时候加1,因为路径上的结点个数比树枝数(码长)多一
5.解码:通过压缩文件内容,从树根向下找,直到找到叶子结点,将字符替换,然后再从根开始找,直到解码完成。注意,这里不能直接使用编码进行字符串替换,因为你不知道是从哪里分割开的。
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Main {
public char[] names;//存取出现的字符种类
public int[] weights;//存取每个字符对应总共出现的次数
public static void main(String[] args) throws IOException {
//1.根据用户输入选择要读取的文件文本文件
System.out.println("请输入要测试的编号:");
Scanner input = new Scanner(System.in);
int testNum = input.nextInt();
Main test = new Main();
BufferedReader reader = new BufferedReader(new FileReader(".\\input_assign03_0"+testNum+".dat"));
StringBuffer s = new StringBuffer();
String ss = "";
while((ss = reader.readLine())!=null){//按行读取
s.append(ss);
}
//2.统计文本中出现的字符及其对应的出现次数作为它的权重,将其存储到对应的数组中
Map<Character,Integer> map = test.countChar(s.toString());
test.names = new char[map.size()];