一、哈/赫夫曼树的基本定义
(1)路径:从树中一个结点到另一个结点之间的分支构成这两个结点之间的路径。
(2)路径长度:路径上的分支数目。
(3)树的路径长度:从树根到每一个结点的路径长度之和。(完全二叉树即为树的路径长度最短的二叉树)
(4)结点的带权路径长度:从该结点到树根之间的路径长度和结点上权的乘积。
(5)树的带权路径长度:树的所有结点的带权路径之和(WPL,Weighted Path Length )。
假设有 n 个权值构建一颗有 n 个叶子结点的二叉树,每个叶子结点带权 ,则其中带权路径长度 WPL 最小的二叉树称为 最优二叉树或哈/赫夫曼树。根据结点的个数,权值的不同,最优二叉树的形状也各不相同,但共同点是:**带权值的结点都是叶子结点。权值越小的结点,其到根结点的路径越长。**叶子上的权值均相同时,完全二叉树一定是最优二叉树,否则完全二叉树不一定是最优二叉树,同时 最优二叉树的形态不唯一。
例如,给定4个叶子结点a,b,c和d,分别带权7,5,2和4。构造如下图所示的三棵二叉树:
(a)WPL=7 * 2+5 * 2+2 * 2+4 * 2=36
(b)WPL=7 * 3+5 * 3+2 * 1+4 * 2=46
(c)WPL=7 * 1+5 * 2+2 * 3+4 * 3=35
其中©树的WPL最小,即可以验证其是哈夫曼树。
二、哈/赫夫曼树的创建
(1)从小到大进行排序,每个数据都是一个结点 , 每个结点可以看成是一棵最简单的二叉树(左右结点都为空的二叉树);
(2)取出根结点权值最小的两棵二叉树,组成一棵新的二叉树, 该新的二叉树的根结点的权值是前面两棵二叉树根节点权值的和
(3)再将这棵新的二叉树, 以根结点的权值大小再次排序, 不断重复以上的步骤, 直到数组中, 所有的数据都被处理, 即可得到一棵哈夫曼树。
三、代码实现
package Tree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class HuffmanTree {
/**
* @param arr 需要创建成哈夫曼树的数组
* @return 创建好后的哈夫曼树的root结点
*/
public static Node huffmantree(int[] arr) {
List<Node> list=new ArrayList<>();
for(int value:arr) {
list.add(new Node(value));//将Node结点值放入ArrayList中
}
while(list.size()>1) {//最后一个结点直接加入即可
Collections.sort(list);//Collections.sort方法用于升序排序
//取出根结点权值最小的两颗二叉树
Node leftNode=list.get(0);
Node rightNode=list.get(1);
//构建一棵新二叉树,其权值为权值最小的两个结点之和
Node parent=new Node(leftNode.value+rightNode.value);
//父结点需要连接上左右两个结点
parent.left=leftNode;
parent.right=rightNode;
//将已经使用过的两个权值最小的结点从list中删除,避免重复
list.remove(leftNode);
list.remove(rightNode);
//将新形成的父结点加入其中,并开始下一次循环重新排序
list.add(parent);
}
return list.get(0);//最后list中只剩下哈夫曼树的root结点
}
public static void preOrder(Node root) {
if (root != null) {
root.preOrder();
} else {
System.out.println("该树为空树,无法遍历!");
}
}
public static void main(String[] args) {
int[] arr= {13,7,8,3,29,6,1};
Node root=huffmantree(arr);
preOrder(root);
}
}
class Node implements Comparable<Node>{//实现Comparable接口,重写compareTo方法
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) {//Node1的构造函数
this.value=value;
}
@Override
public String toString() {//重写toString方法
return "Node [value=" + value + "]";
}
@Override
public int compareTo(Node n) {
return this.value-n.value;//升序排序(n.value-this.value为降序排序)
}
}
运行结果:
Node [value=67]
Node [value=29]
Node [value=38]
Node [value=15]
Node [value=7]
Node [value=8]
Node [value=23]
Node [value=10]
Node [value=4]
Node [value=1]
Node [value=3]
Node [value=6]
Node [value=13]
创建的哈夫曼树如下图所示: