public class Haff {
private int node; // 结点的值
private int parent; // 父结点的值
private int llink; // 左孩子结点的值
private int rlink; // 右孩子结点的值
private int mark; // 标记结点下标,解码时候方便
}
下面是用Java写的哈夫曼编码二叉树的生成过程:
public void generatorTree(List list){
int length = (list.size()+1)/2;
for (int i = 0; i < length-1; i++) {
// x,y为最小两个数组的下标,min1,min2为Integer类型最大值,方便比较的大小
int min1 = MAXINT, min2 = MAXINT, x = ELEINDEX, y = ELEINDEX;
// 找出指定链表长度内最小的两个数
for (int j = 0; j < length + i; j++) {
if (list.get(j).getParent() == -1 && min1 > list.get(j).getNode()) {
y = x;
min2 = min1;
min1 = list.get(j).getNode();
x = j;
} else if (list.get(j).getParent() == -1 && min2 > list.get(j).getNode()) {
min2 = list.get(j).getNode();
y = j;
}
}
list.get(x).setParent(length + i);
list.get(y).setParent(length + i);
list.get(length + i).setNode(min1 + min2);
list.get(length + i).setLlink(x);
list.get(length + i).setRlink(y);
}
}
从代码中我们可以看出,每次循环需要找到已知链表长度中两个最小的结点,然后生成新的结点加入到链表中。
下面是根据已经生成的哈夫曼树生成哈夫曼编码的过程(采用递归的方式实现):
利用递归的方法,生成HaffMan编码
public void generatorCode(List list,int index,StringBuilder strb,Map map){
if(list.get(index).getRlink() == -1 || list.get(index).getLlink() == -1){
map.put(list.get(index).getMark(), strb.toString());
return;
}
strb.append("0");
generatorCode(list,list.get(index).getLlink(),strb,map);
strb.deleteCharAt(strb.length()-1);
strb.append("1");
generatorCode(list,list.get(index).getRlink(),strb,map);
strb.deleteCharAt(strb.length()-1);
}
也可以使用栈从而采用非递归的方式实现。
package algo;
public class Haff {
private int node;
private int parent;
private int llink;
private int rlink;
private int mark;
public Haff(){
this.node = 0;
this.parent = -1;
this.llink = -1;
this.rlink = -1;
this.mark = -1;
}
public int getMark() {
return mark;
}
public void setMark(int mark) {
this.mark = mark;
}
public int getNode() {
return node;
}
public void setNode(int node) {
this.node = node;
}
public int getParent() {
return parent;
}
public void setParent(int parent) {
this.parent = parent;
}
public int getLlink() {
return llink;
}
public void setLlink(int llink) {
this.llink = llink;
}
public int getRlink() {
return rlink;
}
public void setRlink(int rlink) {
this.rlink = rlink;
}
}
package algo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HaffTree {
private final static int[] array = new int[256];
private final static int MAXINT = Integer.MAX_VALUE;
private final static int ELEINDEX = -1;
static {
Arrays.fill(array, 0);
}
// 初始化列表
public List initTree(File file){
int length = 0;
int size = array.length;
List list = new ArrayList(size);
// 打开文件读取流,统计文件中的各个字母出现的次数
try(FileInputStream in = new FileInputStream(file)){
int c;
while((c = in.read()) != -1){
array[c]++;
}
}catch(IOException ex){
System.err.println(ex);
}
// 因为HaffMan树是满二叉树, 初始化list,链表的长度为叶子节点加内部节点。内部节点等于叶子节点减一
for (int i = 0; i < size; i++) {
Haff haff = new Haff();
if (array[i] != 0) {
haff.setNode(array[i]);
haff.setMark(i);
list.add(haff);
length++;
}
}
for(int i = 0; i < length-1; i++){
list.add(new Haff());
}
return list;
}
// HaffMan树
public Map generatorTree(List list){
int length = (list.size()+1)/2;
for (int i = 0; i < length-1; i++) {
// x,y为最小两个数组的下标,min1,min2为Integer类型最大值,方便比较的大小
int min1 = MAXINT, min2 = MAXINT, x = ELEINDEX, y = ELEINDEX;
// 找出指定链表长度内最小的两个数
for (int j = 0; j < length + i; j++) {
if (list.get(j).getParent() == -1 && min1 > list.get(j).getNode()) {
y = x;
min2 = min1;
min1 = list.get(j).getNode();
x = j;
} else if (list.get(j).getParent() == -1 && min2 > list.get(j).getNode()) {
min2 = list.get(j).getNode();
y = j;
}
}
list.get(x).setParent(length + i);
list.get(y).setParent(length + i);
list.get(length + i).setNode(min1 + min2);
list.get(length + i).setLlink(x);
list.get(length + i).setRlink(y);
}
StringBuilder strb = new StringBuilder();
Map map = new HashMap();
// 根据HaffMan树,生成HaffMan编码
generatorCode(list,list.size()-1,strb,map);
return map;
}
// 利用递归的方法,生成HaffMan编码
public void generatorCode(List list,int index,StringBuilder strb,Map map){
if(list.get(index).getRlink() == -1 || list.get(index).getLlink() == -1){
map.put(list.get(index).getMark(), strb.toString());
return;
}
strb.append("0");
generatorCode(list,list.get(index).getLlink(),strb,map);
strb.deleteCharAt(strb.length()-1);
strb.append("1");
generatorCode(list,list.get(index).getRlink(),strb,map);
strb.deleteCharAt(strb.length()-1);
}
// 根据HaffMan编码生成新的文件
public void getNewFile(File inFile,File outFile,Map map){
try(FileInputStream in = new FileInputStream(inFile);
FileOutputStream out = new FileOutputStream(outFile)){
int c;
while((c = in.read()) != -1){
if(map.containsKey(c)){
out.write(map.get(c).getBytes());
}
}
}catch(IOException ex){
System.err.println(ex);
}
}
// 还原HaffMan编码
public void enGeneratorCode(File file,File inFile,List list){
// 打开要读取和写入的文件
try(FileInputStream in = new FileInputStream(inFile);
FileOutputStream out = new FileOutputStream(file)){
int temp = 0,index = list.size()-1,node = 0;
// 从根节点根据读入的字符遍历
while((node = in.read()) != -1){
temp = getNextNode(list, index, node);
if(list.get(temp).getLlink() == -1){
out.write((char)list.get(temp).getMark());
index = list.size()-1;
}else{
index = temp;
}
}
}catch(IOException ex){
System.err.println(ex);
}
}
public int getNextNode(List list,int index,int node){
if(node == 48){
return list.get(index).getLlink();
}else{
return list.get(index).getRlink();
}
}
}
package algo;
import java.io.File;
import java.util.List;
import java.util.Map;
public class readFile {
public static void main(String[] args){
HaffTree haffTree = new HaffTree();
File inFile = new File("E:/user.txt");
File outFile = new File("E:/userCode.txt");
File file = new File("E:/default.txt");
// 获得HaffMan编码
List list = haffTree.initTree(inFile);
Map map = haffTree.generatorTree(list);
// 根据HaffMan编码输出新的文件
haffTree.getNewFile(inFile, outFile,map);
// 还原HaffMan编码
haffTree.enGeneratorCode(file, outFile, list);
}
}