一、基本介绍
1)
给定
n
个权值作为
n
个
叶子结点
,构造一棵二叉树,若该树的
带权路径长度
(
wpl
)
达到最小,称这样的二叉树为
最优二叉树
,也称为
哈夫曼树
(Huffman Tree)
,
还有的书翻译为
霍夫曼树
。
2)
赫夫曼树是带权路径长度最短的树,
权值较大
的结点离
根
较近。
3)
路径
和
路径长度
:在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的
通路
,称为路径。通路中
分支的数目
称为路径长度。若规定根结点的层数为
1
,则从根结点到第
L
层结点的路径长度为
L-1
4)
结点的权及带权路径长度:
若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的
权
。
结点的带权路径长度
为:从根结点到该结点之间的路径长度与该结点的权的
乘积
3)
树的带权路径长度:
树的带权路径长度规定为
所有
叶子结点
的
带权路径长度之和
,记为
WPL
(weighted path length) ,
权值越大的结点离根结点越近的二叉树才是
最优二叉树
。
4)
WPL
最小的就是赫夫曼树
二、构成赫夫曼树的步骤
1)
将
每一个
数据从小到大进行
排序
,
每个数据都是一个节点,
每个节点可以看成是一颗最简单的二叉树(最开始只有根)
2)
取出根节点权值最小的两颗二叉树
3)
组成一颗新的二叉树
,
该新的二叉树的根节点的权值是前面两颗二叉树根节点权值的和
4)
再将这颗新的二叉树,以根节点的权值大小再次排序, 不断重复
1-2-3-4
的步骤,直到数列中所有的数据都被处理,就得到一颗赫夫曼树
三、代码思路
1.首先把待生成哈夫曼树的数列转为一个个node节点,保存到list中,方便后续处理。
2.对list进行排序,取前两个node(这两个权是最小的),构建一个父节点
3.新构建的父节点保存到list中,参与下次排序,而两个孩子节点删掉
4.重复执行 2-3 ,直到list中只剩一个node,这个node就是哈夫曼树的根节点了
四、代码演示
//生成哈夫曼树,返回根节点
public static HuffmanNode createHuffmanTree(int[] arr){
List<HuffmanNode> nodes=new ArrayList<>();
//首先把所有节点放入一个list中,便于操作
for(int val:arr) nodes.add(new HuffmanNode(val));
//重复执行下面的操作,直到list中只剩一个节点
while (nodes.size()>1) {
//排序
Collections.sort(nodes);
//取权值最小的两个节点
HuffmanNode leftNode = nodes.get(0);
HuffmanNode rightNode = nodes.get(1);
//在这两个节点的基础上构建一个二叉树
HuffmanNode parent = new HuffmanNode(leftNode.getWeight() + rightNode.getWeight());
parent.setLeftNode(leftNode);
parent.setRightNode(rightNode);
//把根节点加入list,删掉左右子节点
nodes.add(parent);
nodes.remove(leftNode);
nodes.remove(rightNode);
}
//最后剩下的就是权值最大的节点,也就是根节点
return nodes.get(0);
}
}
//哈夫曼树中的节点
class HuffmanNode implements Comparable<HuffmanNode>{
private int weight;//权重
private HuffmanNode leftNode;
private HuffmanNode rightNode;
public HuffmanNode(int weight) {
this.weight = weight;
}
public HuffmanNode getLeftNode() {
return leftNode;
}
public void setLeftNode(HuffmanNode leftNode) {
this.leftNode = leftNode;
}
public HuffmanNode getRightNode() {
return rightNode;
}
public void setRightNode(HuffmanNode rightNode) {
this.rightNode = rightNode;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "HuffmanNode{" +
"weight=" + weight +
'}';
}
//自定义排序规则(从小到大)
@Override
public int compareTo(HuffmanNode o) {
return this.weight-o.weight;
}
}
测试:
public static void main(String[] args) {
int[] arr={13, 7, 8, 3, 29, 6, 1};
preOrder(createHuffmanTree(arr));
//67,29,38,15,7,8,23,10,4,1,3,6,13
}
//前序遍历
public static void preOrder(HuffmanNode node){
System.out.println(node);//根
if(node.getLeftNode()!=null){//左
preOrder(node.getLeftNode());
}
if(node.getRightNode()!=null){//右
preOrder(node.getRightNode());
}
}
HuffmanNode{weight=67}
HuffmanNode{weight=29}
HuffmanNode{weight=38}
HuffmanNode{weight=15}
HuffmanNode{weight=7}
HuffmanNode{weight=8}
HuffmanNode{weight=23}
HuffmanNode{weight=10}
HuffmanNode{weight=4}
HuffmanNode{weight=1}
HuffmanNode{weight=3}
HuffmanNode{weight=6}
HuffmanNode{weight=13}