实现一个哈夫曼树的应用程序,可以按照以下每个字符的出现频率(权值){空格和26个英文字母出现频率分别为:186,64,13,22,32,103,21,15,47,57,1,5,32,20,57,63,15,1,48,51,80,23,8,18,1,16,1}创建一棵哈夫曼树(要求左孩子的权值小于等于右孩子的权值);并输出所有报文字符的编码,并将“THIS PROGRAM IS MY FAVORITE”编码,统计总的报文传输长度。在此基础上,对用户输入的01串,回答其代表的字符串(即进行报文解码),若解码失败则回复“解码失败”。
编码类:
public class Code {
//编码用数组
int[] bit;
//编码的起始下标
int start;
//字符的权值
int weight;
public Code(int n) {
bit = new int[n];
start = n - 1;
}
}
哈夫曼树类:
public class HaffmanTree {
//最大权值
static final int maxValue = 1000;
//叶结点个数
private int nodeNum;
public HaffmanTree(int n) {
nodeNum = n;
}
/**
* 构造权值为weight的哈夫曼树haffTree
* @param weight
* @param node
*/
public void haffman(int[] weight,HaffNode[] node) {
int m1,m2,x1,x2;
int n = nodeNum;
//哈夫曼树haffTree的初始化,n个结点的哈夫曼树有2n - 1 个结点
for(int i = 0;i < 2 * n - 1;i++) {
HaffNode temp = new HaffNode();
if (i < n) {
temp.weight = weight[i];
}else {
temp.weight = 0;
}
temp.parent = 0;
temp.flag = 0;
temp.leftChild = -1;
temp.rightChild = -1;
node[i] = temp;
}
//构造哈夫曼树HaffTree的n - 1个结点
for(int i = 0;i < n - 1;i++) {
m1 = m2 = maxValue;
x1 = x2 = 0;
for(int j = 0;j < n + i;j++) {
if (node[j].weight < m1 && node[j].flag == 0) {
m2 = m1;
x2 = x1;
m1 = node[j].weight;
x1 = j;
}else if(node[j].weight < m2 && node[j].flag == 0) {
m2 = node[j].weight;
x2 = j;
}
}
//将找出两颗权值最小的子树合并为一颗子树
node[x1].parent = n + i;
node[x2].parent = n + i;
node[x1].flag = 1;
node[x2].flag = 1;
node[n + i].weight = node[x1].weight + node[x2].weight;
node[n + i].leftChild = x1;
node[n + i].rightChild = x2;
}
}
/**
* 由哈夫曼树构造哈夫曼编码
* @param node
* @param haffCode
*/
public void haffmanCode(HaffNode[] node,Code[] haffCode) {
int n = nodeNum;
Code cd = new Code(n);
int child,parent;
//求n个结点的哈夫曼编码
for(int i = 0;i < n;i++) {
//不等长编码的最后一位为n - 1
cd.start = n - 1;
//取得编码对应的权值
cd.weight = node[i].weight;
child = i;
parent = node[child].parent;
//由叶结点向上直到根结点循环
while(parent != 0) {
if(node[parent].leftChild == child) {
//左孩子结点编码0
cd.bit[cd.start] = 0;
}else {
//右孩子结点编码1
cd.bit[cd.start] = 1;
}
cd.start--;
child = parent;
parent = node[child].parent;
}
Code temp = new Code(n);
//保存叶结点的编码和不等长编码的起始位
for(int j = cd.start + 1;j < n;j++) {
temp.bit[j] = cd.bit[j];
}
temp.start = cd.start;
temp.weight = cd.weight;
haffCode[i] = temp;
}
}
}
哈夫曼树结点类:
public class HaffNode {//哈夫曼树的结点类
//权值
int weight;
//标记
int flag;
//双亲结点下标
int parent;
//左孩子下标
int leftChild;
//右孩子下标
int rightChild;
//无参构造函数
public HaffNode() {
}
}
功能实现测试类:
import java.util.*;
public class HaffmanTreeTest {
public static void main(String[] args) {
//构造哈夫曼树的所有叶结点数量
int LeafNodeNumber = 27;
//创建哈夫曼树对象haffmanTree
HaffmanTree haffmanTree = new HaffmanTree(LeafNodeNumber);
//创建记录叶结点权值的整型数组,并进行赋值
int[] weight = {186,64,13,22,32,103,21,15,47,57,1,5,32,20,57,63,15,1,48,51,80,23,8,18,1,16,1};
//创建数组leafNode用于记录叶结点元素数据
Character[] leafNode = {' ','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'};
//创建哈夫曼树节点类型对象,用于记录每一个节点的相关信息
HaffNode[] node = new HaffNode[2 * LeafNodeNumber - 1];
//创建编码类型数组haffmanCode用于记录每个叶结点编码生成的哈夫曼编码
Code[] haffCode = new Code[LeafNodeNumber];
//创建哈希表对象hashMap
Map<Character, String> hashMap = new HashMap();
//调用构造权值为weight,结点元素为node的哈夫曼树所用函数haffman()
haffmanTree.haffman(weight, node);
//调用哈夫曼编码函数,将得到的哈夫曼树进行编码,并将编码信息存放在数组haffCode中
haffmanTree.haffmanCode(node, haffCode);
//设置临时变量temp
String temp;
System.out.println("叶结点元素\t" + "叶结点权值\t" + "哈夫曼编码");
//打印空格+26个大写字母对应的哈夫曼编码
for (int i = 0;i < LeafNodeNumber;i++) {
System.out.print("“" + leafNode[i] + "”");
//每循环一次则清空记录每个叶结点哈夫曼编码的临时变量temp
temp = "";
System.out.print("\t\tWeight = " + haffCode[i].weight + " \tCode = ");
for (int j = haffCode[i].start + 1;j < LeafNodeNumber;j++) {
System.out.print(haffCode[i].bit[j]);
temp += haffCode[i].bit[j];
}
hashMap.put(leafNode[i], temp);
System.out.println();
}
System.out.println();
System.out.println("“THIS PROGRAM IS MY FAVORITE”的哈夫曼编码报文为:");
//运用toCharArray()函数将字符串分隔为单个字符并存储在字符类型数组中
char[] ch = "THIS PROGRAM IS MY FAVORITE".toCharArray();
//创建字符类型的迭代器iterator
Iterator<Character> iterator;
//新建整型变量sum用于记录字符串的哈夫曼编码译文长度
int sum = 0;
for (int i = 0;i < ch.length;i++) {
iterator = hashMap.keySet().iterator();
//调用hasNext()函数判断是否迭代完毕
while (iterator.hasNext()) {
//获取哈希表的相应key值
char key = (char) iterator.next();
//若相等,则sum增加相应长度
if (key == ch[i]) {
//sum增加相应长度
sum += hashMap.get(key).length();
//输出字符串的哈夫曼编码译文
System.out.print(hashMap.get(key));
}
}
}
System.out.println();
System.out.println();
System.out.println("报文的哈夫曼编码译文总长度为:" + sum);
System.out.println();
System.out.println("用户输入: ");
Scanner sc = new Scanner(System.in);
System.out.println("请输入编码形式的字符串(仅由0和1组成):");
//使用字符串类型变量str记录用户输入的字符串
String str = sc.nextLine();
//用于记录有哈夫曼编码反编译的字符串
String text = "";
//
char[] nums = str.toCharArray();
String numberTemp = "";
for (int i = 0; i < nums.length; i++) {
numberTemp += nums[i];
iterator = hashMap.keySet().iterator();
while (iterator.hasNext()) {
char key = (char) iterator.next();
if (hashMap.get(key).equals(numberTemp)) {
text += key;
numberTemp = "";
}
}
}
//如果将译文编译后的结果为空值,则说明编译失败,返回“解码失败”
if (!numberTemp.equals("")) {
text = "解码失败";
}
//输出转译结果
System.out.println("译文为: " + text);
System.out.println("程序结束。");
}
}
构造得到的哈夫曼树如下: