给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
重要概念路径:从一个节点到它往下可以达到的节点所经shu过的所有节点,称为两个节点之间的路径
路径长度:即两个节点的层级差,如A节点在第一层,B节点在第四层,那它们之间的路径长度为4-1=3
权重值:为树中的每个节点设置一个有某种含义的数值,称为权重值(Weight),权重值在不同算法中可以起到不同的作用
节点的带权路径长度:从根节点到该节点的路径长度与该节点权重值的乘积
树的带权路径长度:所有叶子节点的带权路径长度之和,也简称为WPL
哈夫曼树判断
判断一棵树是不是哈夫曼树只要判断该树的结构是否构成最短带权路径。
在下图中3棵同样叶子节点的树中带权路径最短的是右侧的树,所以右侧的树就是哈夫曼树。
代码实现
案例:将数组{13,7,8,3,29,6,1}转换成一棵哈夫曼树
思路分析:从哈夫曼树的概念中可以看出,要组成哈夫曼树,权值越大的节点必须越靠近根节点,所以在组成哈夫曼树时,应该由最小权值的节点开始。
步骤:
(1) 将数组转换成节点,并将这些节点由小到大进行排序存放在集合中
(2) 从节点集合中取出权值最小的两个节点,以这两个节点为子节点创建一棵二叉树,它们的父节点权值就是它们的权值之和
(3) 从节点集合中删除取出的两个节点,并将它们组成的父节点添加进节点集合中,跳到步骤(2)直到节点集合中只剩一个节点
public class HuffmanTreeDemo { public static void main(String[] args) { int array[] = {13,7,8,3,29,6,1}; HuffmanTree huffmanTree = new HuffmanTree(); Node root = huffmanTree.create(array); huffmanTree.preOrder(root); } } //哈夫曼树 class HuffmanTree{ public void preOrder(Node root){ if (root == null){ System.out.println("哈夫曼树为空,无法遍历"); return; } root.preOrder(); } /** * 创建哈夫曼树 * @param array 各节点的权值大小 * @return */ public Node create(int array[]){ //先将传入的各权值转成节点并添加到集合中 List<Node> nodes = new ArrayList<>(); for (int value : array){ nodes.add(new Node(value)); } /* 当集合中的数组只有一个节点时,即集合内所有节点已经组合完成, 剩下的唯一一个节点即是哈夫曼树的根节点 */ while (nodes.size() > 1){ //将节点集合从小到大进行排序 //注意:如果在节点类没有实现Comparable接口,则无法使用 Collections.sort(nodes); //在集合内取出权值最小的两个节点 Node leftNode = nodes.get(0); Node rightNode = nodes.get(1); //以这两个节点创建一个新的二叉树,它们的父节点的权值即是它们的权值之和 Node parent = new Node(leftNode.weight + rightNode.weight); parent.left = leftNode; parent.right = rightNode; //再从集合中删除已经组合成二叉树的俩个节点,并把它们俩个的父节点加入到集合中 nodes.remove(leftNode); nodes.remove(rightNode); nodes.add(parent); } //返回哈夫曼树的根节点 return nodes.get(0); } } //因为要在节点的集合内,以节点的权值value,从小到大进行排序,所以要实现Comparable<>接口 class Node implements Comparable<Node>{ int weight;//节点的权值 Node left; Node right; public Node(int weight) { this.weight = weight; } public void preOrder(){ System.out.println(this); if (this.left != null){ this.left.preOrder(); } if (this.right != null){ this.right.preOrder(); } } @Override public String toString() { return "Node{" + "weight=" + weight + '}'; } @Override public int compareTo(Node o) { return this.weight - o.weight; } }