学习材料:《2013年王道论坛计算机考研机试指南》
文中涉及对学习材料的摘录,以及自己的理解
1 基本概念
路径:1棵树中,从一个结点到另一个结点的通路
路径长度:该路径上所需经过的边的个数
带权路径长度:假设树中结点有权值,那么从根节点到达该节点的路径长度再乘以该结点权值,就是该结点的带权路径长度
树的带权路径长度和:1棵树所有叶子结点的带全路径长度和
哈夫曼树:对于给定的N个结点(有固定权值),以它们为叶子结点构造的二叉树,其中带权路径和最小的一棵二叉树,就是哈夫曼树,同时也被称为最优树。
对于给定N个结点(叶子结点)的哈夫曼树不唯一。
2 哈夫曼树求法
1) 将所有结点放入集合K;
2) 若K中剩余结点>2个, 从K中取出权值最小的2个结点, 创建一个新结点, 使取出的两个结点为该新结点的左右儿子, 设定它的权值为这两个儿子结点的权值和, 并将该新结点放入集合K.
3) 重复2), 直到K中只有1个结点, 它就是所构造的哈弗曼树的根节点; 同时, 过程中所有创建的中间结点(非叶子节点)的权值和, 就是该哈弗曼树的带权路径和.
如下二叉树, 各节点就用其权值的数值表示:
6
/ \
3 3
/ \
1 2
给定叶子结点为: 1,2,3
构造过程: K集合: 1,2,3 ; 取1,2, 新结点权值3, K集合: 3,3; 取3,3,新结点权值6, K集合: 6
带权路径和: 3 + 6 = 9 从叶子结点到根节点带权路径的求法: 1*(1+1) + 2*(1+1) + 3*1 = 9
3 求解哈夫曼树过程可利用的数据结构——优先队列
普通队列:先进先出; 优先队列:优先级高的先出
求解哈夫曼树问题:要获得集合中权值最小的结点,使用堆栈数据结构,即每次要弹出n个元素中最小的元素,因此可直接用优先队列来实现。
新建一个这样的堆:
priority_queue<int> Q; //大顶堆,即这样建立的堆,从中取元素,默认取整个堆中最大的元素。
priority_queue<int, vector<int>, greater<int>> Q; //小顶堆,默认先取得该堆中最小的元素
堆有关操作:
Q.push(x); //将int型元素x放入堆Q中
int a = Q.top(); //取出堆顶元素,即最小元素,保存在a中
Q.pop(); //弹出堆顶元素,取出后堆会自动调整为1个新的小顶堆
/*在运用标准模板库queue时,必须包含如下语句:*/
#include <queue>
using namespace std;
4 题目
/*
* 知识点:哈弗曼树
* 题目:给定n个叶节点及其权值, 求所构造得到的哈弗曼树的带权路径和
* 输入: 2行, 第一行输入一个数n(2<= n <= ),表示叶节点个数; 第二行输入这n个叶节点的权值
* 输出: 哈弗曼树的带权路径和
* 测试用例:
input:
5
1 2 2 5 9
output:
37 (1+2=3 2+3=5 5+5=10 9+10=19 3+5+10+19=37)
* 程序思路:利用优先队列创建小顶堆,把叶节点权值放入小顶堆,依次取出2个最小的权值,相加后存入小顶堆,最后把中间过程两两相加和再求和
*/
#include
#include
#include
using namespace std;
priority_queue, greater> Q; //创建小顶堆, 头文件必须包含才能使用greater标识符 否则默认的只能使用less(那样是大顶堆)
int main()
{
int n;
int i;
int huff_sum;
while( scanf("%d",&n) != EOF )
{
while( Q.empty()==false ) //初始化 清空堆中元素 因为要输入多组数据
Q.pop();
for(i=1; i<=n; i++)
{
int x;
scanf("%d",&x); //scanf会忽略空格,scanf把空格,回车等默认为一个数的结束 所以再输入第二行权值时 可以采用 1 2 2 5 9空格隔开的方式
Q.push(x);
}
huff_sum = 0;
while( Q.size() >=2 )
{
int a = Q.top();
Q.pop();
int b = Q.top();
Q.pop();
huff_sum += a+b;
Q.push(a+b);
}
printf("%d\n", huff_sum);
}
return 0;
}
5 哈夫曼树的应用
哈夫曼树编码、多个数的两两合并