JAVA实现哈夫曼编码

任务1:对已知字符及其权值(频率)创建哈夫曼树并可以打印出每个字符的哈夫曼编码值
任务2:从控制台输入一个字符串,计算出字符的权值,转换为任务一

实现过程:

在哈夫曼树中,所有字符节点都作为叶节点,具有字符内容和频率的属性;将字符节点合成树后,具有左右节点的属性。
所以可以创建出Node节点类:

class Node{
		Node left;//根节点
		Node right;//根节点
		String str;//此节点的字符
		int weight;//权值或频率
	}

构造哈夫曼树:
1.将节点按照权值排序(从小到大或从大到小都行),将排序后结果存储到(队列、数组)
2.从该节点(排好序的)序列中,选择权值最小的两个节点,将其合成一个子树(子树的权值之和为两个节点的权值之和)
3.将这子树看成新的节点重复1、2步骤直到所有节点合成一个树

排序算法

//排序从小到大
	public void sort(Node[] nodes) {
		Node temp = new Node();
		for(int i=0;i<nodes.length;i++) {
			for(int j=i+1;j<nodes.length;j++) {
				if(nodes[i].weight>nodes[j].weight) {
					temp = nodes[j];
					nodes[j] = nodes[i];
					nodes[i] = temp;
				}
			}
		}
	}

用冒泡排序每一轮选出具有最小的权值的节点

根据已知序列权值建树,返回根节点
String[ ] strs 是序列字符数组, int[ ] weights 是字符权值数组,此时是指一一对应的,strs[ i ] 对应的权值是 weights[ i ].

	public Node createHFM(String[] strs,int[] weights ) {
		//转换成Node数组
		Node [] nodes = new Node[strs.length];
		for(int i = 0;i<strs.length;i++) {
			nodes[i] = new Node();
			nodes[i].weight=weights[i];
			nodes[i].str=strs[i];
		}
		while(nodes.length>1) {
			//排序
			sort(nodes);
			Node n1 = nodes[0];
			Node n2 = nodes[1];
			Node node = new Node();
			node.left = n1;
			node.right = n2;
			node.weight = n1.weight+n2.weight;
			//把n1和n2从数组中删除,把node加进去
			Node[] nodes2 = new Node[nodes.length-1];
			for(int i=2;i<nodes.length;i++) {
				nodes2[i-2]=nodes[i];
			}
			nodes2[nodes2.length-1]=node;
			nodes = nodes2;
		}
		Node root = nodes[0];
		return root;
	}

while 循环中,对nodes数组进行排序,结果是节点按权值从小到大顺序存储在nodes中。合成一个子树需要三个节点,其中node作为根节点,从nodes数组中选择最小权值的两个节点分别作为左子节点、右子节点,显而易见就是nodes数组的前两个节点。
合成子树后,需要一个新的数组来存放这些节点并排序,显然数组的长度减少了一个,将之前的nodes数组中剩下的节点复制到新数组中,将合成的子树添加在后面,让nodes指向新数组nodes2。
循环一直进行下去,新数组的长度会越来越少,直到最后只有一个节点就是所有节点都合成为树的时候。所以循环条件是nodes.length>1。
这时,一个哈夫曼树就完成了,根据枝干左0右1,遍历树的时候就可以输出字符的哈夫曼编码值。

打印算法

public void printHFMcode(Node root,String code) {
		if(root!=null) {
			//先序遍历
			if(root.left==null&&root.right==null) {
				String msg = root.str+"权值"+root.weight+" HFM编码:"+code;
				System.out.println(msg);
			}
			printHFMcode(root.left,code+"0");
			printHFMcode(root.right,code+"1");
		}
	}

任务1举例:

public static void main(String[] args) {
		HfmTree hfm = new HfmTree();
		String [] strs = {"A","B","C","D","E","F"};
		int[] weights =  { 4 , 6 , 1 , 9 , 8 , 2 };
		Node root = hfm.createHFM(strs, weights);
		hfm.printHFMcode(root, "");

在这里插入图片描述
任务2中需要的新算法:

//根据已知字符串序列创建HashMap
	public Map<String,Integer> CreateHashMap(String str){
		Map<String, Integer> hm = new HashMap<String, Integer>();
		for(int i=0;i<str.length();i++) {
			char s = str.charAt(i);//取出i位置上的字符
			String key = String.valueOf(s);//将字符类型转换为字符串类型
			if(hm.get(key)==null) {
				//map中没有字符串key,则添加字符串key进去并设置频率(value)为1
				hm.put(key, 1);
			}else {
				//map中已存在字符串key,则将其频率(value)增加1
				hm.put(key, hm.get(key)+1);
			}
		}
		return hm;
	}
//根据HashMap得到字符串数组
	public String[] StrfromHash(Map<String,Integer> map) {
		int i=0;
		String skey[] = new String[map.size()];
		Set<String> keys = map.keySet();
		Iterator<String> it = keys.iterator();
		while(it.hasNext()) {
			skey[i++]=it.next();
		}
		return skey;
	}
//根据HashMap得到权值数组
	public int[] weightfromHash(Map<String,Integer> map) {
		int i=0;
		int [] weight = new int[map.size()];
		Set<String> keys = map.keySet();
		Iterator<String> it = keys.iterator();
		while(it.hasNext()) {
			String s = it.next();
			weight[i++]=map.get(s);
		}
		return weight;
	}

在这里需要使用到HashMap,通过键值对可以存储字符与权值一一对应的对象,key–value。
根据HashMap获得字符串数组和权值数组,就可以套入任务1中的方法中了。
得到字符串数组需要使用HashMap的keySet()方法,该方法是将HashMap中的所有key对象放在Set中返回,这个集合是无序的(元素存储的位置顺序由哈希码来确定),元素是唯一不重复的。因此要获取其中元素,需要使用迭代器,Iterator.
hasNext方法是来判断迭代器中是否有元素,有则返回true
next是返回下一个元素。
skey[i++] = it.next();可以将所有的元素都存到是skey数组中。
而得到权值数组则有一点不同,因为要做到一一对应,所以在while循环中要使用到HashMap的get()方法,将对应于当前元素的value值取出,才能存入weight数组中。

任务2举例:

System.out.println("输入字符串:");
		Scanner sc = new Scanner(System.in);
		String string = sc.next();
		Map<String,Integer> map = hfm.CreateHashMap(string);
		String str[] = hfm.StrfromHash(map);
		int weight[] =hfm.weightfromHash(map);
		System.out.println("打印出的字符及其权值和哈希值为:");
		for(int i=0;i<map.size();i++) {
			System.out.println(str[i]+"--"+weight[i]+"--"+str[i].hashCode());
		}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值