这个高级版指的是从SourceFile文件中读字符,再将这些文字压缩到CodeFile文件中,最后从CodeFile文件中根据编码将其解压出来。
主要过程:
1.先将从文件中读取的字符放入到HashMap中,用HashMap来记录每个字符出现的次数,相当于是权重,
2.创建哈夫曼树,(过程跟前面写过的博客简易哈夫曼树是一样的,这里就不多说了)
3.在得到每个叶子结点的编码的时候,再用一个HashMap将叶子结点和和它对应的编码存在一起
4.然后再读一遍SourceFile,按照顺序将出现的文字的编码都用StringBuilder连接起来,存入到CodeFile文件中。这个StringBuilder就是文件中一一对应的编码
5.最后根据CodeFile中的StringBuilder将文字解析出来,从字符的最左边开始,如果是0就走左子树,如果是1就走右子树,但是我们每找到一个叶子结点,都需要重新返回到整棵树的根结点,但是指向StringBuilder的指针位置不能变,所以就从主函数中使用一个循环,每当找到叶子结点的时候,就返回它在StringBuilder中的位置,以便于下次进行查找。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
class Node1 {
int weight;
Node1 Lchild;
Node1 Rchild;
Node1 parent;
String val;
public Node1(){
}
public Node1(String val,int weight) {
this.val=val;
this.weight=weight;
}
}
public class HuffmanTree2 {
static String res="";
static HashMap<String,String> map1=new HashMap<>();
public static void main(String[] args) throws IOException {
//从文件中读字符
FileInputStream fis=new FileInputStream("C:\\Users\\lenovo\\Desktop\\SourceFile.txt");//从这个文件中读取数据
byte[] bytes=new byte[3];
int readCount=0;
HashMap<String, Integer> map=new HashMap<>();
//将文字都读入map集合中,统计出现的数量
System.out.println("SourceFile文件内:");
while((readCount=fis.read(bytes))!=-1) {
String s=new String(bytes,0,readCount);
System.out.print(s);
map.put(s,map.getOrDefault(s,0)+1);
}
System.out.println("");
ArrayList<Node1> list=new ArrayList<>();
//使用list将Map集合中的结点都存进去
for(Map.Entry<String,Integer> m:map.entrySet()) {
Node1 node1=new Node1(m.getKey(),m.getValue());
list.add(node1);
}
list.sort((o1, o2) -> o1.weight-o2.weight);
//建树
Node1 root=CreateTree(list);
String s="";
System.out.println("每个字的编码:");
print(root,s);
//从文件中按照顺序拿到字符的编码
FileInputStream fis2=new FileInputStream("C:\\Users\\lenovo\\Desktop\\SourceFile.txt");//从这个文件中读取数据
byte[] bytes3=new byte[3];
int readCount1=0;
while((readCount1=fis2.read(bytes3))!=-1) {
String s2=new String(bytes3,0,readCount1);
res+=map1.get(s2);
}
//将编码写入到CodeFile文件内
FileOutputStream fos=new FileOutputStream("C:\\Users\\lenovo\\Desktop\\CodeFile.txt");
byte[] bytes1=res.getBytes();
fos.write(bytes1);
//从编码文件中读取编码
FileInputStream fis1=new FileInputStream("C:\\Users\\lenovo\\Desktop\\CodeFile.txt");
byte[] bytes2=new byte[2];
int readcount=0;
StringBuilder s1= new StringBuilder();
while((readCount=fis1.read(bytes2))!=-1) {
s1.append(bytes2[0]-48);
if(readCount==2)s1.append(bytes2[1]-48);
}
System.out.println("CodeFile文件内:");
System.out.println(s1);
int i=0;
System.out.println("解压后:");
while(i<s1.length()) {
i=jiema(s1,i,root);
}
}
public static Node1 CreateTree(ArrayList<Node1> list) {
while(list.size()!=1) {
Node1 temp1=list.remove(0);
Node1 temp2=list.remove(0);
Node1 par=new Node1();
par.weight=temp1.weight+temp2.weight;
par.Lchild=temp1;
par.Rchild=temp2;
temp1.parent=par;
temp2.parent=par;
list.add(par);
list.sort((o1, o2) -> o1.weight-o2.weight);
}
Node1 node=list.get(0);
return node;
}
public static void print(Node1 root,String s) {
if(root==null)
return ;
if(root.Rchild==null&&root.Lchild==null) {
//走到根结点的时候
System.out.println(root.val+":"+s);
map1.put(root.val,s);
}
print(root.Lchild,s+"0");
print(root.Rchild,s+"1");
}
//按照压缩后的编码进行解码
public static int jiema(StringBuilder s,int i,Node1 root) {
if(root.Lchild==null&&root.Rchild==null) {
System.out.print(root.val);
return i;
}
if(s.charAt(i)-'0'==0&&i<s.length()) {
return jiema(s,i+1,root.Lchild);
}
if(s.charAt(i)-'0'==1&&i<s.length()) {
return jiema(s,i+1,root.Rchild);
}
return 0;
}
}