哈夫曼编码26个大写字符及空格、打印字符串的哈夫曼编码译文长度、哈夫曼编码反编译。

     实现一个哈夫曼树的应用程序,可以按照以下每个字符的出现频率(权值){空格和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("程序结束。");
	}
}

构造得到的哈夫曼树如下:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 13
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值