java数据结构实现文本的哈夫曼编码HuffmanTree(用数组的方式)
•实验内容
1、读取文本Demo.txt。统计'a'-'z',空格,'.'的出现次数,并计算出权重。
2、根据字符及其权重构造Huffman树的节点 (Huffman节点需要的各个属性及相关方法)。
3、构造一个链表保存这些节点。
4、分两次从链表中找出权值最小的节点,生成一颗子树,并计算该子树的高度 (引入节点的Height属性)。
5、当链表中最后只剩一个节点时,就获得了Huffman树的根。
6、前序遍历Huffman树,遍历时构造Huffman编码(引入节点的编码属性)。
7、将特定文本翻译成Huffman编码对应的01串。
8、将上一步的01串通过Huffman树翻译成文本。
•实现代码
1.设计Chars类
package 实验三;
public class Chars
{
char thechar;//文本中的字符
float weight;//字符的权重
int[] huffmancode;//哈弗曼树的编码
int height;
//构造方法,初始化,产生一个huffman编码
Chars()
{
this.thechar='\0';
this.weight=0.0f;
this.huffmancode=null;
}
//获取字符
public char getThechar() {
return thechar;
}
//设置字符
public void setThechar(char thechar) {
this.thechar = thechar;
}
//获取权重
public float getWeight() {
return weight;
}
//设置权重
public void setWeight(float weight) {
this.weight = weight;
}
//获取huffmancode
public int[] getHuffmancode() {
return huffmancode;
}
//设置huffmancode
public void setHuffmancode(int[] huffmancode) {
this.huffmancode =huffmancode;
}
//toString()方法
public String toString()
{
return "thechar:"+thechar+" weight:"+weight+" height:"+height+" huffmancode:";
}
}
2.设计HuffmanList类
package 实验三;
public class HuffmanList
{
HuffmanNode huffmanNode;
float weight;
int totalnodes;//连表头结点总数
//链表中增加结点
void addNode(HuffmanNode huffmanNode,float weight)
{
this.huffmanNode=huffmanNode;
this.weight=weight;
}
}
3.设计HuffmanNode类
package 实验三;
public class HuffmanNode implements Comparable<HuffmanNode>//Huffman树的结点类描述
{
public float weight;// 结点的数据域
public int height;//哈夫曼树的高度
public int flag;//结点是否加入哈夫曼树的标志
public HuffmanNode parent,lchild,rchild;//父结点及左右孩子结点域
char letter;//读入的字符
int[] huffmancode;//读入字符编码
public HuffmanNode()//构造一个空结点
{
this.weight=0.0f;
this.lchild=null;
this.rchild=null;
this.height=0;
this.letter='\0';
this.huffmancode=null;
}
public float getWeight()
{
return weight;
}
public void setWeight(float weight)
{
this.weight = weight;
}
public int getHeight()
{
return height;
}
public void setHeight(int height)
{
this.height = height;
}
public HuffmanNode getParent()
{
return parent;
}
public void setParent(HuffmanNode parent)
{
this.parent = parent;
}
public HuffmanNode getLchild()
{
return lchild;
}
public void setLchild(HuffmanNode lchild)
{
this.lchild = lchild;
}
public HuffmanNode getRchild()
{
return rchild;
}
public void setRchild(HuffmanNode rchild)
{
this.rchild = rchild;
}
public char getLetter()
{
return letter;
}
public void setLetter(char letter)
{
this.letter = letter;
}
public int[] getHuffmancode()
{
return huffmancode;
}
public void setHuffmancode(int[] huffmancode)
{
this.huffmancode = huffmancode;
}
//构造一个左、右孩子域为空,具有权值的结点
public HuffmanNode(int weight)
{
this.weight=weight;
flag=0;
parent=lchild=rchild=null;
}
//构造一个带有字符和权重的结点
public HuffmanNode(char letter,float weight)
{
this.letter=letter;
this.weight=weight;
}
@Override
//两结点权重比较,返回值为1或0或-1
public int compareTo(HuffmanNode node) //比较权重是否相同
{
int result = 1;//初始result设置为1
if(this.getWeight()>node.getWeight())//若当前权重大于比较的那个的权重,输出1
{
result=1;
}
else if(this.getWeight()<node.getWeight())//若当前权重小于比较的那个的权重,输出-1
{
result= -1;
}
else//当两个的权重相等的时候,则比较它们的深度
{
if(this.getWeight()>node.getHeight())//若当前深度大于比较的那个的深度,输出1
result=1;
else if(this.getHeight()<node.getHeight())//若当前深度小于比较的那个的深度,输出 -1
result= -1;
}
return result;
}
}
4.设计HuffmanTree
package 实验三;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
public class HuffmanTree
{
char Chars[];//存放字符权重的数组
HuffmanList Hlist;//链表
//计算每个字符的最终权重(该字母出现次数/所有字符出现次数的总和),并存入HuffmanList中
static void computeWeight(Chars[] chars)
{
HuffmanList Hlink=new HuffmanList();//创建列表
float totalWeight=0.0f;//总的权重
for(int i=0;i<chars.length;i++)
{//在链表的长度范围内
//总权重=每个字符的权重相加
totalWeight=totalWeight+chars[i].getWeight();
}
for(int i=0;i<chars.length;i++)
{//在链表的长度范围内
//第i个字符的权重=这个字符的出现次数/所有字符出现的次数
chars[i].setWeight(chars[i].getWeight()/totalWeight);
if(i<26)//小于26的时候,就是26个字母。起始是a,+1就变成b,+2就变成c,如此增加i,确定是哪个字母
Hlink.addNode(new HuffmanNode('a'+i),chars[i].getWeight());
if(i==26)//空格,排在26
Hlink.addNode(new HuffmanNode(' '),chars[i].getWeight());
if(i==27)//.排在27
Hlink.addNode(new HuffmanNode('.'),chars[i].getWeight());
if(i==28)//,排在28
Hlink.addNode(new HuffmanNode(','),chars[i].getWeight());
}
}
//构建Huffman树
int [][] conHuffmanTree(Chars[] chars)
{
int n=chars.length;//字符个数
int m=2*n-1;//哈夫曼树的结点数
HuffmanNode[] HN=new HuffmanNode[m];
int i;
//当i小于字符个数的时候,用一个HuffmanNode数组把各个结点给存起来
for(i=0;i<n;i++)
{
HN[i]=new HuffmanNode();//构造n个具有权值的结点
HN[i].letter=chars[i].thechar;//设置字符
HN[i].weight=chars[i].weight;//设置权重
}
for(i=n;i<m;i++)//建造哈夫曼树
{//每一次都把权值最小的那个结点拿进来构造树
HuffmanNode min1=getMin(HN,i-1);
min1.flag=1;//进树,flag=1
HuffmanNode min2=getMin(HN,i-1);
min2.flag=1;//进树,flag=1
//构造min1和min2的父结点,并修改其父结点的权值
HN[i]=new HuffmanNode();
min1.parent=HN[i];
min2.parent=HN[i];
//min1和min2进行比较,小的放左边,大的放右边
if(min1.compareTo(min2)==-1)
{//当min1小于min2的时候
HN[i].lchild=min1;
HN[i].rchild=min2;
}
else//当min1大于min2的时候
{
HN[i].lchild=min2;
HN[i].rchild=min1;
}
HN[i].weight=min1.weight+min2.weight;//父节点权值=左+右
chars[i-28].height=getHeight(HN[i]);//设置高度
}
//从叶子到根逆向求每个字符的哈夫曼编码
int[][] HuffCode=new int[n][n];//分配n个字符编码存储空间
for(int j=0;j<n;j++)
{
int start=n-1;//编码的开始位置,初始化为数组的结尾
for(HuffmanNode c=HN[j],p=c.parent;p!=null;c=p,p=p.parent)
{
//从叶子到根逆向求编码
if(p.lchild.equals(c))//左孩子编码为0
HuffCode[j][start--]=0;
else//(右孩子编码为1)
HuffCode[j][start--]=1;
HuffCode[j][start]=-1;//编码的开始标志为-1,编码是-1之后的0、1序列
}
}
//当i小于字符个数的时候
for(i=0;i<n;i++)
{
//第i个结点的huffmancode对应chars数组的第i个编码
chars[i].huffmancode=HuffCode[i];
}
return HuffCode;//返回结点的数组
}
//获得最小权重的结点
static HuffmanNode getMin(HuffmanNode[] HN,int end)
{
//先把最后一个默认为是权重最小的
HuffmanNode min=HN[end];
for(int i=0;i<=end;i++)
{
//用一个新的结点把第i个结点取出来进行权重大小的比较
HuffmanNode h=HN[i];
//不在哈夫曼树中且weight最小的结点
if(h.flag==0&&h.weight<min.weight)
min=h;
}
return min;//返回权值最小的结点
}
//获取高度
public int getHeight(HuffmanNode node)
{
if(node == null){
return 0;
}
int i = getHeight(node.lchild);
int j = getHeight(node.rchild);
return (i<j)? j+1:i+1;
}
//将读进来的文本以字符串形式接受
public static String txt2String(File file)
{
StringBuilder result = new StringBuilder();
try
{
//构造一个BufferedReader类来读取文件
BufferedReader br = new BufferedReader(new FileReader(file));
String s = null;
while((s = br.readLine())!=null)
{//使用readLine方法,一次读一行
result.append(System.lineSeparator()+s);
}
br.close();
}
catch(Exception e)
{
e.printStackTrace();
}
return result.toString();
}
public static void main(String[] args)
{
HuffmanTree Huff=new HuffmanTree();
Chars[] chars=new Chars[29];
for(int i=0;i<chars.length;i++)
chars[i]=new Chars();
File txtfile =new File( "D:\\Demo\\Java\\eclipse\\DSPracticeDemo\\Demo.txt");//找到文件所在的位置
try
{
FileReader read=new FileReader(txtfile);
int inputChar;
while ((inputChar=read.read())!=-1)
{
//当字符是在a到z之间的时候
if((inputChar>='a')&&(inputChar<='z'))
{
chars[inputChar-'a'].setThechar((char) inputChar);
chars[inputChar-'a'].setWeight(chars[inputChar-'a'].getWeight()+1);//获取权重
}
//当字符为空格
else if(inputChar==' ')
{
chars[26].setThechar(' ');
chars[26].setWeight(chars[26].getWeight()+1);
}
//当字符为'.'
else if(inputChar=='.')
{
chars[27].setThechar('.');
chars[27].setWeight(chars[27].getWeight()+1);
}
//当字符为','
else if(inputChar==',')
{
chars[28].setThechar(',');
chars[28].setWeight(chars[28].getWeight()+1);
}
}
computeWeight(chars);//算出权重
//循环输出
HuffmanTree T=new HuffmanTree();//构造哈夫曼树
T.conHuffmanTree(chars);//求哈夫曼编码
System.out.println("哈夫曼编码为:");
for(int i=0;i<chars.length;i++)
{
System.out.print("下标为:"+i+" ");
System.out.print(chars[i]);
//将特定文本翻译成Huffman编码对应的01串。
for(int j=0;j<chars[i].huffmancode.length;j++)
{
if(chars[i].huffmancode[j]==-1)//开始标识符读到数组结尾
{
for(int k=j+1;k<chars[i].huffmancode.length;k++)
System.out.print(chars[i].huffmancode[k]);//输出
break;
}
}
System.out.println();
}
//将特定文本翻译成Huffman编码对应的01串。
System.out.print("将特定文本翻译成Huffman编码对应的01串:");
String str=txt2String(txtfile);
//System.out.println(str);
//将字符串转为数组
char[] array = str.toCharArray();
int maxsize=array.length;//最大的长度
int[] codeArray=new int[maxsize];//建一个数组,用来存放编码
int codestr = 0;
//输出a
for(int l=0;l<array.length;l++)
{
for(int m=0;m<chars.length;m++)
{
if(array[l]==(chars[m].thechar))
{
for(int j=0;j<chars[m].huffmancode.length;j++)
{
if(chars[m].huffmancode[j]==-1)//开始标识符读到数组结尾
{
for(int k=j+1;k<chars[m].huffmancode.length;k++)
{
codeArray[l]=chars[m].huffmancode[k];//编码存进去数组中
//System.out.print(chars[m].huffmancode[k]);//输出
System.out.print(codeArray[l]);
}
break;
}
}
}
}
if(l%20==0)//一行输出20个就好了
System.out.println();
}
System.out.println("");
//将上一步的01串通过Huffman树翻译成文本。
System.out.print("将上一步的01串通过Huffman树翻译成文本:");
//输出
for(int q=0;q<array.length;q++)
{
for(int m=0;m<chars.length;m++)
{
if(array[q]==(chars[m].thechar))
{
for(int j=0;j<chars[m].huffmancode.length;j++)
{
if(chars[m].huffmancode[j]==-1)//开始标识符读到数组结尾
{
for(int k=j+1;k<chars[m].huffmancode.length;k++)
{
codeArray[q]=chars[m].huffmancode[k];//编码存进去数组中
codestr=chars[m].huffmancode[k];
}
if(codeArray[q]==codestr)
{
System.out.print(chars[m].thechar);
}
break;
}
}
}
}
if(q%100==0)//一行输出100个就好了
System.out.println();
}
//Chars[] charsSingle=Huff.getContext(txtfile); //获取数据
read.close();
}
catch (Exception e)
{
System.out.println(e.toString());
}
}
}
5.测试类
package 实验三;
import java.io.File;
import java.io.FileReader;
public class Test
{
public static void main(String[] args)
{
HuffmanTree Huff=new HuffmanTree();
Chars[] chars=new Chars[29];
for(int i=0;i<chars.length;i++)
chars[i]=new Chars();
File txtfile =new File( "D:\\Demo\\Java\\eclipse\\DSPracticeDemo\\Demo.txt");//找到文件所在的位置
try
{
FileReader read=new FileReader(txtfile);
int inputChar;
while ((inputChar=read.read())!=-1)
{
//当字符是在a到z之间的时候
if((inputChar>='a')&&(inputChar<='z'))
{
chars[inputChar-'a'].setThechar((char) inputChar);
chars[inputChar-'a'].setWeight(chars[inputChar-'a'].getWeight()+1);//获取权重
}
//当字符为空格
else if(inputChar==' ')
{
chars[26].setThechar(' ');
chars[26].setWeight(chars[26].getWeight()+1);
}
//当字符为'.'
else if(inputChar=='.')
{
chars[27].setThechar('.');
chars[27].setWeight(chars[27].getWeight()+1);
}
//当字符为','
else if(inputChar==',')
{
chars[28].setThechar(',');
chars[28].setWeight(chars[28].getWeight()+1);
}
}
Huff.computeWeight(chars);//算出权重
//循环输出
HuffmanTree T=new HuffmanTree();//构造哈夫曼树
T.conHuffmanTree(chars);//求哈夫曼编码
System.out.println("哈夫曼编码为:");
for(int i=0;i<chars.length;i++)
{
System.out.print("下标为:"+i+" ");
System.out.print(chars[i]);
//将特定文本翻译成Huffman编码对应的01串。
for(int j=0;j<chars[i].huffmancode.length;j++)
{
if(chars[i].huffmancode[j]==-1)//开始标识符读到数组结尾
{
for(int k=j+1;k<chars[i].huffmancode.length;k++)
System.out.print(chars[i].huffmancode[k]);//输出
break;
}
}
System.out.println();
}
//将特定文本翻译成Huffman编码对应的01串。
System.out.print("将特定文本翻译成Huffman编码对应的01串:");
String str=Huff.txt2String(txtfile);
//System.out.println(str);
//将字符串转为数组
char[] array = str.toCharArray();
int maxsize=array.length;//最大的长度
int[] codeArray=new int[maxsize];//建一个数组,用来存放编码
int codestr = 0;
//输出a
for(int l=0;l<array.length;l++)
{
for(int m=0;m<chars.length;m++)
{
if(array[l]==(chars[m].thechar))
{
for(int j=0;j<chars[m].huffmancode.length;j++)
{
if(chars[m].huffmancode[j]==-1)//开始标识符读到数组结尾
{
for(int k=j+1;k<chars[m].huffmancode.length;k++)
{
codeArray[l]=chars[m].huffmancode[k];//编码存进去数组中
//System.out.print(chars[m].huffmancode[k]);//输出
System.out.print(codeArray[l]);
}
break;
}
}
}
}
if(l%50==0)//一行输出50个就好了
System.out.println();
}
System.out.println("");
//将上一步的01串通过Huffman树翻译成文本。
System.out.print("将上面的01串通过Huffman树翻译成文本:");
//输出
for(int q=0;q<array.length;q++)
{
for(int m=0;m<chars.length;m++)
{
if(array[q]==(chars[m].thechar))
{
for(int j=0;j<chars[m].huffmancode.length;j++)
{
if(chars[m].huffmancode[j]==-1)//开始标识符读到数组结尾
{
for(int k=j+1;k<chars[m].huffmancode.length;k++)
{
codeArray[q]=chars[m].huffmancode[k];//编码存进去数组中
codestr=chars[m].huffmancode[k];
}
if(codeArray[q]==codestr)
{
System.out.print(chars[m].thechar);
}
break;
}
}
}
}
if(q%150==0)//一行输出150个就好了
System.out.println();
}
read.close();
}
catch (Exception e)
{
System.out.println(e.toString());
}
}
}
运行结果: