最优二叉树(赫夫曼数)
Posted 顧棟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最优二叉树(赫夫曼数)相关的知识,希望对你有一定的参考价值。
最优二叉树(赫夫曼数)
定义
- 什么叫路径?
从树中一个结点到另一个结点之间的分支构成这两个结点之间的路径。
一个祖先结点到子孙结点之间的分支构成这两个结点的路径。
- 什么叫路径长度?
路径上的分支数量称为路径长度。
- 什么是树的路径长度?
树的路径长度是从树根到每一结点的路径长度之和。
n n n个结点的二叉树中,完全二叉树就是最短路径长度的二叉树。
- 什么是结点的权
给每一个结点赋予一个新的数值,被称为这个结点的权。
- 结点的带权路径长度是什么?
结点的带权路径长度为从该结点到树根之间路径长度与结点上权的乘积。
树的带权路径长度为树中所有叶子结点的带权路径长度之和。 记做 W P L WPL WPL。
假设有 n n n个权值 { W 1 , W 2 , ⋅ ⋅ ⋅ , W n } \\mathrm{\\{W_1,W_2,···,W_n \\}} {W1,W2,⋅⋅⋅,Wn},试图构造一颗有 n n n个叶子结点的二叉树,每个叶子结点带权值 W i \\mathrm{W_i} Wi,其中带权路径长度 W P L WPL WPL最小的二叉树称为最优二叉树。
赫夫曼树表现不唯一。
如何构建赫夫曼树
(1)根据给定的n个权值 { W 1 , W 2 , ⋅ ⋅ ⋅ , W n } \\mathrm{\\{W_1, W_2,···,W_n\\}} {W1,W2,⋅⋅⋅,Wn}构成n棵二叉树的集合 F = { T 1 , T 2 , ⋅ ⋅ ⋅ , T n } F=\\{T_1,T_2,···,T_n\\} F={T1,T2,⋅⋅⋅,Tn},其中每颗二叉树 T i T_i Ti中只有一个带权 W i W_i Wi的根结点,其左右子树均空。
(2)在 F F F中选取两棵根结点的权值最小的树作为左、右子树构成一棵新的二叉树,且置新的二叉树的根结点的权值为其左、右子树上根结点的权值之和。
(3)在 F F F中删除这两棵树,同时将新得到的二叉树加入到F中。
(4)重复(2)和(3),直到F只含有一棵树位置,此时的树即赫夫曼数。
JAVA实现
构建哈夫曼树时,需要每次根据各个结点的权重值,筛选出其中值最小的两个结点,然后构建二叉树。
查找权重值最小的两个结点的思想是:从树组起始位置开始,首先找到两个无父结点的结点(说明还未使用其构建成树),然后和后续无父结点的结点依次做比较,有两种情况需要考虑:
- 如果比两个结点中较小的那个还小,就保留这个结点,删除原来较大的结点;
- 如果介于两个结点权重值之间,替换原来较大的结点;
package tree;
/**
* @author
* @date
*/
public class HuffmanTreeNode {
/**
* 值
*/
private final Integer value;
private HuffmanTreeNode left;
private HuffmanTreeNode right;
/**
* 权值
*/
private final int weight;
public HuffmanTreeNode(Integer value, int weight) {
this.value = value;
this.weight = weight;
}
@Override
public String toString() {
return "HuffmanTreeNode[value=" + this.value + ", weight=" + this.weight + "]";
}
public Integer getValue() {
return value;
}
public HuffmanTreeNode getLeft() {
return left;
}
public void setLeft(HuffmanTreeNode left) {
this.left = left;
}
public HuffmanTreeNode getRight() {
return right;
}
public void setRight(HuffmanTreeNode right) {
this.right = right;
}
public int getWeight() {
return weight;
}
}
package tree;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
/**
* @author
* @date
*/
public class HuffmanTree {
public static void main(String[] args) {
List<HuffmanTreeNode> nodes = new ArrayList<HuffmanTreeNode>();
nodes.add(new HuffmanTreeNode(1, 40));
nodes.add(new HuffmanTreeNode(2, 8));
nodes.add(new HuffmanTreeNode(3, 10));
nodes.add(new HuffmanTreeNode(4, 30));
nodes.add(new HuffmanTreeNode(5, 10));
nodes.add(new HuffmanTreeNode(6, 2));
HuffmanTreeNode root = HuffmanTree.createTree(nodes);
System.out.println(breadthFirst(root));
}
public static HuffmanTreeNode createTree(List<HuffmanTreeNode> nodes) {
// 结点数据nodes,还满足构造一个二叉树
while (nodes.size() > 1) {
sortNodes(nodes, 0, nodes.size() - 1);
HuffmanTreeNode left = nodes.get(nodes.size() - 1);
HuffmanTreeNode right = nodes.get(nodes.size() - 2);
HuffmanTreeNode parent = new HuffmanTreeNode(null, left.getWeight() + right.getWeight());
parent.setLeft(left);
parent.setRight(right);
nodes.remove(nodes.size() - 1);
nodes.remove(nodes.size() - 1);
nodes.add(parent);
}
return nodes.get(0);
}
/**
* 对结点进行排序
*
* @param nodes
*/
private static void sortNodes(List<HuffmanTreeNode> nodes, int start, int end) {
if (start < end) {
HuffmanTreeNode base = nodes.get(start);
int i = 0;
int j = end + 1;
while (true) {
while (i < end && nodes.get(++i).getWeight() >= base.getWeight()) {
}
while (j > start && nodes.get(--j).getWeight() <= base.getWeight()) {
}
if (i < j) {
swap(nodes, i, j);
} else {
break;
}
}
swap(nodes, start, j);
//递归左边子序列
sortNodes(nodes, start, j - 1);
//递归右边子序列
sortNodes(nodes, j + 1, end);
}
}
private static void swap(List<HuffmanTreeNode> nodes, int i, int j) {
HuffmanTreeNode tmp;
tmp = nodes.get(i);
nodes.set(i, nodes.get(j));
nodes.set(j, tmp);
}
//广度优先遍历
public static List<HuffmanTreeNode> breadthFirst(HuffmanTreeNode root){
Queue<HuffmanTreeNode> queue = new ArrayDeque<>();
List<HuffmanTreeNode> list = new ArrayList<>();
if(root!=null){
//将根元素加入“队列”
queue.offer(root);
}
while(!queue.isEmpty()){
//将该队列的“队尾”元素加入到list中
list.add(queue.peek());
HuffmanTreeNode p = queue.poll();
//如果左子节点不为null,将它加入到队列
assert p != null;
if(p.getLeft() != null){
queue.offer(p.getLeft());
}
//如果右子节点不为null,将它加入到队列
if(p.getRight() != null){
queue.offer(p.getRight());
}
}
return list;
}
}
参考
《数据结构 (C语言版)》 严蔚敏版
以上是关于最优二叉树(赫夫曼数)的主要内容,如果未能解决你的问题,请参考以下文章
构建Huffman哈夫曼最优二叉树,binarytree,Python