赫夫曼树
1.基本介绍
(1)给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度(WPL : weighted path length)达到最小,称这样的二叉树为最优二叉树,也称哈夫曼树(Huffman Tree)。
(2)哈夫曼树是带权路径长度最短的树,权值较大的结点距离根结点较近。
2.赫夫曼树的几个重要概念
(1)路径:在一棵树中,从一个结点往下可以达到的孩子或者孙子结点之间的通路,称之为路径。
(2)路径长度:通路中分支(节点)的数目,称之为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
(3)节点的权:若将树中结点赋予一个有着某种含义的数值,则这个数值称为该结点的权。
(4)带权路径长度:某个结点的带权路径长度为,从根结点到该结点的路径长度与该结点权值的乘积。
(5)树的带权路径长度:树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL(weighted path length),权值越大的结点距离根结点越近的二叉树才是最优二叉树。
(6)WPL最小的就是赫夫曼树
3.赫夫曼树创建思路
总结一下构建赫夫曼树的步骤
-
- 从小到大进行排序,每个数据都是一个节点,每个节点可以看成是一棵最简单的二叉树。
-
- 取出根节点权值最小的两棵二叉树,并移除掉。
-
- 将取出来的两个二叉树组成一棵新的二叉树,这个新二叉树根节点的权值是前面两棵二叉树根节点的权值之和。
-
- 再将这棵新的二叉树,以根节点权值大小放入到数列中,再次排列,不断重复 1-2-3-4 步骤,直到数列中所有的数据都被处理,数列中最终只剩下一个节点,也就是最终的根节点,最终就得到一棵赫夫曼树。
4.赫夫曼树的代码实现
package com.zk.datastruct.huffman;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @Description:
* @ClassName: HuffmanTree
* @Author: ZK
* @Date: 2021/1/29 23:42
* @Version: 1.0
*/
public class HuffmanTree {
public static void main(String[] args) {
int[] arr = {13, 7, 8, 3, 29, 6, 1};
Node huffmanTreeRoot = createHuffmanTree(arr);
// 前序遍历一把
huffmanTreeRoot.preOrder();
}
public static Node createHuffmanTree(int[] arr){
// 构建 List<Node> 集合
List<Node> nodes = new ArrayList<>();
for (int val : arr) {
nodes.add(new Node(val));
}
// 构建赫夫曼树的步骤,循环 1-2-3-4 步,直到数列中所有的数据都被处理,数列中最终只剩下一个节点
while(nodes.size() != 1){
// 1.从小到大进行排序
Collections.sort(nodes);
// 2.取出根节点权值最小的两棵二叉树,并移除掉
Node left = nodes.remove(0);
Node right = nodes.remove(0);
// 3.将取出来的两个二叉树组成一棵新的二叉树,这个新二叉树根节点的权值是前面两棵二叉树根节点的权值之和。
Node parent = new Node(left, right);
// 4.再将这棵新的二叉树,以根节点权值大小放入到数列中
nodes.add(parent);
}
return nodes.get(0);
}
}
/**
* 创建节点类
* 为了支持Node对象的排序,要求以 value 属性的值进行排序
* 实现 Comparable 接口,实现 compareTo方法
*/
class Node implements Comparable<Node>{
int value;
Node left;
Node right;
public Node(int value){
this.value = value;
}
public Node(Node left, Node right) {
this.left = left;
this.right = right;
this.value = left.value + right.value;
}
@Override
public String toString() {
return "Node [value = " + this.value + "]";
}
@Override
public int compareTo(Node o) {
// 表示 Node 对象按照 value 属性从小到大进行排序
return this.value - o.value;
// 如果要从大到小排序
// return o.value - this.value;
}
// 前序遍历
public void preOrder(){
System.out.println(value);
if (this.left != null) {
this.left.preOrder();
}
if (this.right != null) {
this.right.preOrder();
}
}
}