1. 基本介绍
- 给定 n 个权值作为 n 个叶子结点,构造一棵二叉树,若该树的带权路径长度(wpl)达到最小,称这样的二叉树为最优二叉树,也成为 哈/赫/霍夫曼树。
- 赫夫曼树是带权路径长度最短的树,权值较大的结点离根较劲。
2. 概念简介
- 路径和路径长度:在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根节点的层数是1,则从根节点到第 L 层结点的路径长度为L - 1
- 结点的权及带权路径长度:若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权(树中某个结点的数值)。结点的带权路径长度为:从根节点到该节点之间的路径长度与该节点的权的乘积。(路径长度 * 权值)
- 树的带权路径长度:树的带权路径长度规定为 所有叶子结点的带权路径之和长度,记为WPL(weighted path length),权值越大的结点离根节点越近的二叉树才是最优二叉树。
- WPL最小的就是赫夫曼树 —— 树的带权路径最小的二叉树是赫夫曼树。
3. 赫夫曼树构建步骤
- 从小到大将每个结点进行进行排序,每个结点都可以看成是一颗最简单的二叉树
- 取出权值最小的两棵二叉树
- 组成一颗新的二叉树,该新的二叉树的根节点的权值是取出的二叉树的权值之和
- 再将这棵新的二叉树以根节点的值再次进行排序,不断重复以上步骤,直到集合中原来的数据都被处理过,就可以得到以可赫夫曼树。
4. 步骤演练
5. 代码
package huffmanTree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class HuffmanTree {
public static void main(String[] args) {
int[] arr = {13,7,8,3,29,6,1};
// 将 list 构造成赫夫曼树
Node res = createHuffmanTree(arr);
preOrder(res);
}
public static Node createHuffmanTree(int[] arr){
// 第一步:将 arr 数组中的数据封装成结点,并将结点可以放到一个链表中
// 方便之后的读取
List<Node> list = new ArrayList<>();
for(int val : arr){
list.add(new Node(val));
}
// 打印一下构造前的list
System.out.println(list);
while(list.size() > 1) {
// 第一步:排序
Collections.sort(list);
// 第二步:取出最小的两棵树
Node leftNode = list.get(0);
Node rightNode = list.get(1);
// 第三步:利用取出的两棵树,构造新的树
Node parent = new Node(leftNode.val + rightNode.val);
parent.left = leftNode;
parent.right = rightNode;
// 删除比较过的两棵树
list.remove(leftNode);
list.remove(rightNode);
// 将新的树放到集合中
list.add(parent);
}
return list.get(0);
}
public static void preOrder(Node root){
if(root != null){
root.preOrder();
}else {
System.out.println("二叉树为空,无法遍历");
}
}
}
class Node implements Comparable<Node> {
int val;
Node left;
Node right;
public Node(int val) {
this.val = val;
}
@Override
public String toString() {
return "Node{" +
"val=" + val +
'}';
}
@Override
public int compareTo(Node o) {
return this.val - o.val;
}
public void preOrder(){
System.out.print(this + " ");
if(this.left != null){
this.left.preOrder();
}
if(this.right != null){
this.right.preOrder();
}
}
}