哈夫曼树
概念:带权路径最短的二叉树,也为最优二叉树;
特点:所有的数据都存放在叶子节点,父节点的权值等于左右子节点的和。
构建:输入的结点数为8,其结点为a、b、c、d、e、f、g、h,其出现的频率(权重)分别为:0.07、0.19、0.02、0.06、0.32、0.03、0.21、0.10。
- 将一组权值(频率)用数组队列存储,并从小到大排序;
- 创建一个父节点,将最小的权值作为作为父节点的左子节点,次小的权值作为父节点的右子节点,父节点的权值=左子节点权值+右子节点权值;并删除数组中的左右子节点的权值
- 将父节点权值加入数组;重复1-3,直到数组仅剩下一个权值,即根节点。
哈弗曼编码:当构建出哈夫曼树之后,得到根节点往下遍历,往左子节点走编为0,右子节点为1,直到遍历到叶子节点,因为我们最终的目的是需要对叶子节点编码的,常常应用在压缩。
这样的编码之后,平均的码长最短短,所以同样的信息传输,所需要的传输时间就最少。
下面有个代码,已经实现Huffman树的构建与编码的,供大家参考。
思路:输入一串字符,首先计算频次,然后排序,建树(结果就会剩下根节点),进行中序遍历,然后进行编码。注意看看,编码,我是自上而下编。
public class HaffumanThree {
public static void main(String[] args) {
String s="";
HaffumanThree haffumantree=new HaffumanThree();
Scanner sc=new Scanner(System.in);
String str=sc.nextLine();
int[] array=haffumantree.count(str);
LinkedList<Node> list=haffumantree.nodelist(array);
//haffumantree.sort(list);
haffumantree.CreatTree(list);
haffumantree.traverse(list.getFirst());
haffumantree.code(s, list.getFirst());
//System.out.println(str.toString());
}
public int[] count(String str)
{
int[] array=new int[256];
for(int i=0;i<str.length();i++)
{
int ascii=str.charAt(i);
array[ascii]++;
}
return array;
}
public LinkedList<Node> nodelist(int[] array)
{
LinkedList<Node> list=new LinkedList<Node>();
String s;
//System.out.println(Arrays.toString(array));
for(int i=0;i<array.length;i++)
{
if(array[i]>0)
{
s=(char)i+"";
Data data=new Data(s, array[i]);
Node node=new Node(data);
list.add(node);
}
}
//System.out.println(list.get(0));
return list;
}
public void sort(LinkedList<Node> list)
{
int min=0,count=list.size();
while(count>0)
{
min=0;
for(int i=0;i<count;i++)
{
if(list.get(min).data.count>list.get(i).data.count)
min=i;
}
Node node=list.remove(min);
list.add(node);
count--;
}
}
public void CreatTree(LinkedList<Node> list)
{
while(list.size()>1)
{
sort(list);
Node node1=list.removeFirst();
Node node2=list.removeFirst();
Data data=new Data(node1.data.str+node2.data.str, node1.data.count+node2.data.count);
Node Parents=new Node(data);
Parents.left=node1;
Parents.right=node2;
list.add(Parents);
}
}
public void traverse(Node node)
{
if(node.left==null&&node.right==null)
System.out.println("节点符号:"+node.data.str+" 权值:"+node.data.count);
if(node.left!=null)
{
traverse(node.left);
}
if(node.right!=null)
{
traverse(node.right);
}
}
public void code(String s,Node node)
{
if(node!=null)
{
code(s+"0",node.left);
if(node.left==null&&node.right==null)
{
System.out.println("节点编码:"+node.data.str+" "+s);
}
code(s+"1",node.right);
}
}
}
测试:
aabbbccddddd
节点符号:d 权值:5
节点符号:b 权值:3
节点符号:a 权值:2
节点符号:c 权值:2
节点编码:d 0
节点编码:b 10
节点编码:a 110
节点编码:c 111