哈夫曼树
基本概念
路径:在一棵树中,从任意一个结点到达另一个结点的通路
路径长度:该路径所需经过的边的个数
带权路径长度:从根结点到达该节点的路径长度再乘以该结点权值的结果
带权路径长度和:树所有的叶子结点的带权路径长度和
哈夫曼树:给定n个带权值结点,以它们为叶子结点构造的一棵带权路径和最小的二叉树
哈夫曼树的求法
- 将所有结点放入集合 K。
- 若集合 K 中剩余结点大于 2 个,则取出其中权值最小的两个结点,构造他们同时为某个新节点的左右儿子,该新节点是他们共同的双亲结点,设定它的权值为其两个儿子结点的权值和。并将该父亲结点放入集合 K。重复步骤 2 或 3。
- 若集合 K 中仅剩余一个结点,该结点即为构造出的哈夫曼树数的根结点,所有构造得到的 中间结点(即哈夫曼树上非叶子结点)的权值和即为该哈夫曼树的带权路径和 。
给定结点的哈夫曼树可能不唯一,所以关于哈夫曼树的机试题往往需要求解的是其最小带权路径长度和。如下例题:
题目描述:
哈夫曼树,第一行输入一个数 n,表示叶结点的个数。需要用这些叶结点生成哈夫曼树,根据哈夫曼树的概念,这些结点有权值,即 weight,题目需要输出所有结点的值与权值的乘积之和。
输入:
输入有多组数据。
每组第一行输入一个数 n,接着输入 n 个叶节点(叶节点权值不超过 100,2<=n<=1000)。
输出:
输出权值。
样例输入:
5
1 2 2 5 9
样例输出:
37
来源:
2010 年北京邮电大学计算机研究生机试真题
解题思路:
由于会存在反复从集合中取出两个最小数,并向集合中存入之前取出两数的和的操作,容易想到使用小根堆可以完成此操作,而且时间复杂度为 log2n .此功能可以使用java的优先队列PriorityQueue完成。
解题步骤
- 将所有节点的权值存入优先队列集合queue中
- 从集合queue集合中取出两个最小数,并求和得到sum(中间节点的权值)
- 将sum存入集合queue 中,并求
ans += sum;(ans保存中间节点的权值和) - 重复1,2,直至集合queue中为空。
- 输出ans,此时ans为最小带权路径长度和,即为题目所求。
代码示例
import java.util.Scanner;
import java.util.PriorityQueue;
import java.util.Queue;
public class Main{
public static void main(String [] args){
Scanner cin = new Scanner(System.in);
while(cin.hasNext()){
int N = cin.nextInt();
Queue<Integer> queue = new PriorityQueue<Integer>(N);
// 输入所有节点的权值
for(int i = 0; i < N; i++){
queue.add(cin.nextInt());
}
int ans = 0; // 保存中间节点的权值之和
// 集合queue中两个最小的值
int min1 = 0;
int min2 = 0;
int sum = 0; // 保存min1和min2的和
while(queue.size() >= 2){
min1 = queue.poll();
min2 = queue.poll();
sum = min1 + min2;
queue.add(sum);
ans += sum;
}
System.out.println(ans);
}
}
}
附:Java PriorityQueue类的常用方法
| 方法 | 类型 | 描述 |
|---|---|---|
| public PriorityQueue(int initialCapacity) | 构造 | 指定容量初始化优先级队列 |
| public boolean add(E e) | 普通 | 向优先队列中插入指定元素 |
| public void clear() | 普通 | 清空优先队列 |
| public E peek() | 普通 | 取出对头元素,但是不删除 |
| public E poll() | 普通 | 对头元素出栈 |
| public int size() | 普通 | 取得优先级队列中元素的个数 |
10万+

被折叠的 条评论
为什么被折叠?



