用哈夫曼树对句子进行编码

package nine;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/*
 * 需求分析:
 * 将句子it-is-a-tall-ill-lia-编程写出这句子的哈弗曼树和哈夫曼码
 * 约束:
 * 1.	权值小的放在左边
 * 2.	权值大的放在右边
 * 3.	权值一样,子树多的放在左边
 * 4.	权值一样,子树少的放在右边
 * 
 * 思路分析:
 * 哈弗曼树的过程:上面句子分析,
 * s(1),t(2),a(3),i(4),l(5),空格 ( 6 )
 * (具体思路代码有写)
 * 
 * 我知道我代码还存在一个这样的问题
 * 1.如果遇到多个权值一样,,可能就无法满足其中的
 * "权值一样,子树多的放在左边"的约束条件
 * 其实有解决思路,就是:在每一个节点再写一个获取它子节点个数的方法
 * 如果对子节点的个数进行比较,确定放在那一边,只是我不想写了 
 */
public class HuffmanTree 
{
	public static void main(String[] args) 
	{
		String str="it is a tall ill lia ";
		HuffmanTree h= new HuffmanTree();
		//获取哈夫曼树
		HuffmanNode root=h.MakeHaffmanTree(str);
		h.pre(root);
		//获取每个字符的哈夫曼编码
		Map<String, String> codes=h.HuffmanCode(root);
		//保存哈夫曼编码
		StringBuffer transform=new StringBuffer();
		char[] mychar=str.toCharArray();
		for(Character k:mychar)
		{
			transform.append(codes.get(String.valueOf(k)));
		}
		System.out.println(transform);
		System.out.println(codes);
	}
	private static Map<String, String> codes=new HashMap<>();
	StringBuffer sectional=new StringBuffer();
	public Map<String, String>  HuffmanCode(HuffmanNode root)
	{
		if(root==null)
		{
			return null;
		}
		HuffmanCode(root.lchild, "0", sectional);
		HuffmanCode(root.rchild, "1", sectional);
		return codes;
	}
	//获得哈夫曼编码
	/**
	 * 这里使用递归获得它的哈夫曼编码的
	 * @param node	这里代表要获得编码的节点,按逻辑来说应该是root开始
	 * @param falg	每一条路径,左是0,右边是1
	 * @param sectional 每一个编码的其中一个子集合,所以每一次都要拼接
	 */
	public void HuffmanCode(HuffmanNode node,String falg,StringBuffer sectional)
	{
		//将传进来的编码子集合保存下来,并将这条路径的编码拼接进去
		StringBuffer nextsectional=new StringBuffer(sectional);
		nextsectional.append(falg);
		if(node!=null)
		{
			//表明还没到叶节点,向左右递归
			if(node.data==null)
			{
				HuffmanCode(node.lchild, "0", nextsectional);
				HuffmanCode(node.rchild, "1", nextsectional);
			}
			//表名已经来到了叶节点
			else
			{
				codes.put(node.data, nextsectional.toString());
			}
		}
	}
	//创建哈夫曼树
	public HuffmanNode MakeHaffmanTree(String str)
	{
		List<HuffmanNode> list =this.ToHuffmanNode(str);
		//这里和上课的思路一样,
		//拿两个权值最小的节点生成一个新的节点,并将新的节点加入list中
		while(list.size()>1)
		{
			//取出两个节点,生成一个大的节点,并将他们生成一颗二叉树
			Collections.sort(list);
			HuffmanNode left=list.get(0);
			HuffmanNode righ=list.get(1);
			if(left.weight==righ.weight&&righ.data==null)
			{
				HuffmanNode temp=left;
				left=righ;
				righ=temp;
			}
			HuffmanNode Big=new HuffmanNode(left.weight+righ.weight, null);
			Big.rchild=righ;
			Big.lchild=left;
			//将小的两个从list中删掉,大的增加进去
			list.remove(righ);
			list.remove(left);
			list.add(Big);	
		}
		//当循环完毕后就只剩下一个节点,而且这个节点就是哈夫曼树的根节点
		//将它放回就完事了
		return list.get(0);
	}
	
	//将这些字母全部转化成节点
	private List<HuffmanNode> ToHuffmanNode(String str)
	{
		//这个是想保存哈夫曼结点的
		List<HuffmanNode> list=new ArrayList<HuffmanNode>();
		//这个是想保存每个字母出现的次数,利用hashmap来统计
		Map<Character, Integer> AllCharacter =new HashMap<Character, Integer>();
		
		char[] a=str.toCharArray();
		for(Character k:a)
		{
			Integer falg=AllCharacter.get(k);
			if(falg==null)
			{
				//证明这个字符第一次扫描到,就将他加进去
				AllCharacter.put(k, 1);
			}
			else
			{
				//证明之前已经加入过了
				AllCharacter.put(k, falg+1);
			}
		}
		//经过上面,就将所有的字符统计出来放到map中,
		//现在是需要将所有的字符创建成一个节点
		for(HashMap.Entry<Character, Integer> k:AllCharacter.entrySet())
		{
			list.add(new HuffmanNode(k.getValue(), String.valueOf(k.getKey())));
		}
		return list;
	}
	//先序遍历哈夫曼树
	public void pre(HuffmanNode root)
	{
		if(root!=null)
		{
			root.pre();
		}
		else
		{
			System.out.println("哈夫曼树是空树");
		}
	}
}
class HuffmanNode implements Comparable<HuffmanNode>
{
	//这是权值
	public int weight;
	//对应的数据
	public String data;
	//左右孩子
	public HuffmanNode lchild;
	public HuffmanNode rchild;
	public HuffmanNode(int weight, String data)
	{
		super();
		this.weight = weight;
		this.data = data;
	}
	@Override
	public String toString()
	{
		return "[weight=" + weight + ", data=" + data + "]";
	}
	//实现对每个节点的排序
	@Override
	public int compareTo(HuffmanNode o)
	{
		return this.weight-o.weight;
	}
	//先序遍历
	public void pre()
	{
		System.out.println(this+"   ");
		if(this.lchild!=null)
		{
			this.lchild.pre();
		}
		if(this.rchild!=null)
		{
			this.rchild.pre();
		}
	}
	
}
//下面是保留测试的代码
/*
for(char k:a)
		{
			System.out.print(k+"##    ");
		}
*/
发布了133 篇原创文章 · 获赞 37 · 访问量 4740

猜你喜欢

转载自blog.csdn.net/qq_43416157/article/details/104233487