赫夫曼树的几个基本概念
(1)给定 n 个权值作为 n 个叶子节点,构造一棵二叉树,若该二叉树的带权路径长度(wlp)达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树 ,或者赫夫曼树。
(2)赫夫曼树是带权路径长度最短的树,权值较大的节点离根较近。
(3)路径和路径长度:在一棵树中,从一个节点往下可以达到的孩子或孙子节点之间的通路,称为路径。通路中分支的熟路称为路径长度。若规定根节点的层数为 1 ,则从根节点到第 L 层节点的路径长度为 L-1;
(4)节点的权及带权路径长度:若树中节点赋给一个有着某种含义的数值,则这个数值称为该节点的权。节点的带权路径长度为:从根节点到该节点之间的路径长度与该节点的权的乘积。
(5)树的带权路径长度:树的带权路径长度规定为所有叶子节点的带权路径长度之和,记为 WPL,权值越大的节点离根节点越近的二叉树才是最优二叉树。
注:WPL 最小的二叉树就是赫夫曼树。
应用实例:给你一个数列{13, 7, 8, 3, 29, 6, 1},要求转成一棵赫夫曼树。
思路分析:
(1)将数据从小到大依次排序,每个数据是一个节点,每个节点看成一棵最简单的二叉树;
(2)取出节点权值最小的两棵二叉树;
(3)组成一棵新的二叉树,该新的二叉树的根节点的权值是前面两棵二叉树根节点权值的和;
(4)再将这棵新的二叉树,以根节点的权值大小再次排序,不断重复 1~4 的步骤,直到数列中所有的数据都被处理,就得到了一棵赫夫曼树。
代码实现:
package tree.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 };
Node root = creatHuffmanTree(arr);
//测试一把
preOrder(root);
}
//创建赫夫曼树的方法
//输入的是需要创建成赫夫曼树的数组 arr
//返回的是创建好之后赫夫曼树的 root 节点
public static Node creatHuffmanTree(int[] arr){
//第一步为了便于操作
//1. 遍历 arr 数组
//2. 将 arr 的每个元素构成一个 Node
//3. 将 Node 放入到 ArrayList
List<Node> nodes = new ArrayList<>();
for (int i = 0; i < arr.length; i++) {
nodes.add(new Node(arr[i]));
}
//处理的过程是循环的过程
while (nodes.size() > 1) {
//排序 从小到大
Collections.sort(nodes);
System.out.println("nodes = " + nodes);
//取出根节点最小的两棵二叉树
//(1)取出取值最小的的节点(二叉树);
Node leftNode = nodes.get(0);
//(2)取出权值第二小的节点(二叉树)
Node rightNode = nodes.get(1);
//(3)构建一棵新的二叉树
Node parent = new Node(leftNode.value + rightNode.value);
parent.left = leftNode;
parent.right = rightNode;
//(4) 从 ArrayList 删除处理过的二叉树
nodes.remove(leftNode);
nodes.remove(rightNode);
//(5) 将 parent 加到 ArrayList
nodes.add(parent);
}
return nodes.get(0);
}
//编写一个前序遍历的方法
public static void preOrder(Node root){
if (root != null){
root.preOrder();
}else {
System.out.println("该树是空树!");
}
}
}
//创建节点类
//为了让 Node 对象持续排序 Collections 集合排序
//让 Node 实现 Comparable
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;
// o.value - this.value表示从大到小排序
}
}