数据结构树之霍夫曼树
霍夫曼树
1、为什么需要霍夫曼树
打个比喻,一篇文章里面每个字母出现的次数不同,有点差距很大。如果在编码的时候把每个字母的编码长度都设为一样,这样当然可行,但是会浪费存储空间。如果存在这样的一种编码,出现次数多的字母编码较少,这样就可以一定长度的节省内存空间。对于这样的编码,我们可以用霍夫曼树来实现
A—>00
B—>100
C—>101
D—>01
E—>11
这样就会发现这样的编码没有公共的前缀(如果存在公共前缀,那么这个编码是不正确的,以为这样在解码的时候会出现无法判断的情况)
2、霍夫曼树构建过程
(1)从序列里取出两个权值最小的节点
(2)创建一个新的节点,这个新的节点的权值是这两个节点的和
(3)把这个新的节点加入序列
(4)判断这个序列的长度是否大于1,如果是,则回到(1),否则结束构建过程
为了每一次都取出最小,插入的时候都是根据从小到大插入正确的位置,我们创建一个新链表,这个链表是单向的,保存了链表头和元素个数。
(1)插入:判断插入的节点中数据项的大小,然后插入正确位置(这样形成的序列是有序的)
(2)删除:删除表头(表头是最小的元素)
数的节点:
class HuffmanNode<T>
{
//保存的数据
T data;
//数据对应的权值
int weight;
//左子树
HuffmanNode<T> left;
//右子树
HuffmanNode<T> right;
//链表中指向的下个节点
HuffmanNode<T> next;
//构造函数
public HuffmanNode(T data,int weight)
{
this.data=data;
this.weight=weight;
}
//
public HuffmanNode(int weight)
{
this.weight=weight;
}
}
链表:
/**
* 在插入的时候判断节点的数据大小,然后插入特定的位置,所以这个链表是已经排好序的(ASC)
* @author faceless
*
*/
class Link<T extends Comparable<T>>
{
//链表头
HuffmanNode<T> frist;
//节点数
int length;
/**
* 插入新节点
* @param node
*/
public void insert(HuffmanNode<T> node)
{
//如果头节点为空,那么先插入头结点
if(this.frist==null)
{
frist=node;
length++;
}
else//头结点不为空,则根据大小插入
{
HuffmanNode<T> current=frist;
//current节点之前的节点
HuffmanNode<T> preCurrent=null;
while(current!=null)
{
if(node.weight>=current.weight)
{
preCurrent=current;
current=current.next;
}
else
{
//插入头结点之前
if(preCurrent==null)
{
node.next=current;
frist=node;
}
else {
node.next=current;
preCurrent.next=node;
}
//插入结束
break;
}
}
//插入到最后
if(current==null)
preCurrent.next=node;
length++;
}
}
/**
* 删除头结点
* @return
*/
public HuffmanNode<T> delete()
{
HuffmanNode<T> frist=this.frist;
this.frist=this.frist.next;
length--;
return frist;
}
/**
* 以这个链表构建霍夫曼树,实际上就是把队列转换为数,最后队列只有一个元素,也就是根
*/
public HuffmanTree<T> createHuffmanTree()
{
while(this.length>1)
{
HuffmanNode<T> nowLeft=this.delete();
HuffmanNode<T> nowRight=this.delete();
HuffmanNode<T> nowRoot=new HuffmanNode<T>(nowLeft.weight+nowRight.weight);
nowRoot.left=nowLeft;
nowRoot.right=nowRight;
//插入新的节点
this.insert(nowRoot);
}
return new HuffmanTree<T>(this.frist);
}
}
createHuffmanTree()方法的目的是把这个链表构成一棵霍夫曼树
3、构建霍夫曼树表示的序列,使用递归来实现
private void createHuffmanCode()
{
this.stringCode(this.root,"");
}
private void stringCode(HuffmanNode<T> node,String string)
{
if(node.data!=null)
{
map.put(node.data, string);
}
else
{
stringCode(node.left,string+"0");
stringCode(node.right,string+"1");
}
}
4、编码和解码:这个就是霍夫曼树的应用,这里就不做介绍