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+"## ");
}
*/
用哈夫曼树对句子进行编码
最新推荐文章于 2021-12-15 14:56:50 发布