什么是堆:堆是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于(或不小于)其左孩子和右孩子节点的值;
首先,堆分为大根堆(最大堆)和小根堆(最小堆)二种;
小根堆满足:
(1)若根结点存在左孩子,则根结点的值小于等于左孩子结点的值;
(2)若根结点存在右孩子,则根结点的值小于等于右孩子结点的值;
(3)以左右孩子为根的子树又各是一个堆;
或者:
根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆;
大根堆与小根堆类似,只需把小于等于改为大于等于就得到了;
如:
哈夫曼树:又称最优二叉树,是一种带权长度最短的二叉树,所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度;
树的带权路径长度记为: WPL=(21*L1+W2*L2+W3*L3+...+Wn*Ln);
如:给定4个叶子结点a,b,c,d,分别带权7,4,5,2,构造如下面所示的三棵二叉树(还有许多种),它们的带权路径长度分别为:
(1)WPL = 7*2+5*2+2*2+4*2;
(2)WPL = 7*1+5*2+2*3+4*3;
(3)WPL = 7*3+5*3+2*1+4*2;
其中(2)的WPL最小,可以验证,它就是哈夫曼树;
因此,在n个带权叶子结点所构成的二叉树中,满二叉树或完全二叉树不一定是最优二叉树,权值越大的结点离根越近的二叉树才是最优二叉树;
构造哈夫曼树:
哈夫曼编码:
求出哈夫曼树后,以上图为例,只需人为规定左侧为0,右侧为1,那么结点9的编码是:00;结点5的编码是:10。哈夫曼编码是一种应用广泛且非常有效的数据压缩技术,该技术一般可将数据文件压缩掉20%-90%,其压缩效率取决于被压缩文件的特征;
Java代码:
package huffman;
/**
*
* 二叉树节点
*/
class Node implements Comparable
{
private int value;
private Node leftChild;
private Node rightChild;
public Node(int value)
{
this.value = value;
}
public int getValue()
{
return value;
}
public void setValue(int value)
{
this.value = value;
}
public Node getLeftChild()
{
return leftChild;
}
public void setLeftChild(Node leftChild)
{
this.leftChild = leftChild;
}
public Node getRightChild()
{
return rightChild;
}
public void setRightChild(Node rightChild)
{
this.rightChild = rightChild;
}
public int compareTo(Object o)
{
Node that = (Node) o;
double result = this.value - that.value;
return result > 0 ? 1 : result == 0 ? 0 : -1;
}
}
package huffman;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class HuffmanTreeBuilder
{
public static void main(String[] args)
{
// 方式1
List<Node> list = new ArrayList<Node>();
list.add(new Node(7));
list.add(new Node(5));
list.add(new Node(4));
list.add(new Node(2));
// 方式2
List<Node> nodes = Arrays.asList(new Node(1), new Node(3), new Node(5),
new Node(7));
Node node = HuffmanTreeBuilder.build(list);
PrintTree(node);
}
/**
*
* 构造哈夫曼树
*
*/
private static Node build(List<Node> nodes)
{
nodes = new ArrayList<Node>(nodes);
sortList(nodes);
while (nodes.size() > 1)
{
createAndReplace(nodes);
}
return nodes.get(0);
}
/**
*
* 组合两个权值最小结点,并在结点列表中用它们的父结点替换它们
*
*/
private static void createAndReplace(List<Node> nodes)
{
Node left = nodes.get(0);
Node right = nodes.get(1);
Node parent = new Node(left.getValue() + right.getValue());
parent.setLeftChild(left);
parent.setRightChild(right);
nodes.remove(0);
nodes.remove(0);
nodes.add(parent);
sortList(nodes);
}
/**
*
* 将结点集合由大到小排序
*/
@SuppressWarnings("unchecked")
private static void sortList(List<Node> nodes)
{
Collections.sort(nodes);
}
/**
*
* 打印树结构,显示的格式是node(left,right)
*
*/
public static void PrintTree(Node node)
{
Node left = null;
Node right = null;
if (node != null)
{
System.out.print(node.getValue());
left = node.getLeftChild();
right = node.getRightChild();
System.out.println("(" + (left != null ? left.getValue() : " ")
+ "," + (right != null ? right.getValue() : " ") + ")");
}
if (left != null)
PrintTree(left);
if (right != null)
PrintTree(right);
}
}