给定 n 个权值作为 n 个叶子结点, 构造一棵二叉树, 若该树的带权路径长度(wpl)达到最小, 称这样的二叉树为最优二叉树, 也称为霍夫曼树(Huffman Tree)。
在正式介绍霍夫曼树之前,先介绍一下带权路径长度WPL(weighted path length):树的带权路径长度规定为所有叶子结点的带权路径长度之和, 记为 WPL ,权值越大的结点离根结点越近的二叉树才是最优二叉树。
路径和路径长度: 在一棵树中, 从一个结点往下可以达到的孩子或孙子结点之间的通路, 称为路径。 通路中分支的数目称为路径长度。 若规定根结点的层数为 1, 则从根结点到第 L 层结点的路径长度为 L-1
结点的权及带权路径长度: 若将树中结点赋给一个有着某种含义的数值, 则这个数值称为该结点的权。 结点的带权路径长度为: 从根结点到该结点之间的路径长度与该结点的权的乘积
比如下图所示三棵树的WPL分别是 规定的4个叶子节点,其权值分别是13,7,8,3,其wpl最小是59,WPL最小的就是赫夫曼树,其特点是权值较大的节点离根节点较近
实现赫夫曼树的步骤
- 从小到大进行排序, 将每一个数据, 每个数据都是一个节点 , 每个节点可以看成是一颗最简单的二叉树
- 取出根节点权值最小的两颗二叉树
- 组成一颗新的二叉树, 该新的二叉树的根节点的权值是前面两颗二叉树根节点权值的和
- 再将这颗新的二叉树, 以根节点的权值大小 再次排序, 不断重复1-4的步骤, 直到数列中, 所有的数据都被处理, 就得到一颗赫夫曼树
赫夫曼树代码实现:
public class HuffmanTreeDemo {
public static void main(String[] args) {
int[] arr = {13, 7, 8, 3};
Node root = creatHuffmanTree(arr);
preOrder(root);
}
public static void preOrder(Node root){
if(root != null) {
root.preOrder();
}else {
System.out.println("null");
}
}
public static Node creatHuffmanTree(int[] arr) {
ArrayList<Node> nodes = new ArrayList<Node>();
for(int value : arr){
nodes.add(new Node(value));
}
while(nodes.size() > 1) {
Collections.sort(nodes);
System.out.println(nodes);
Node leftNode = nodes.get(0);
Node rightNode = nodes.get(1);
Node parent = new Node(leftNode.value + rightNode.value);
parent.left = leftNode;
parent.right = rightNode;
nodes.remove(leftNode);
nodes.remove(rightNode);
nodes.add(parent);
}
return nodes.get(0);
}
}
class Node implements Comparable<Node>{
int value;
Node left;
Node right;
public void preOrder() {
System.out.println(this);
if(this.left != null) {
this.left.preOrder();
}
if(this.right != null) {
this.right.preOrder();
}
}
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return "Node [value=" + value + "]";
}
@Override
public int compareTo(Node o) {
return this.value - o.value;
}
}
实现结果检验,使用前序遍历,可以验证结果正确
|