哈夫曼树介绍
哈夫曼树的求法
哈夫曼树的实现
题目示例
1,介绍:给定n个结点和它们的权值,以它们为叶子结点构造一颗带权路径长度和最小的二叉树,该二叉树即为哈夫曼树
2.哈夫曼树的求法:
(1)将所有结点放入集合K;
(2)若集合K中剩余结点大于2个,则取出其中权值最小的两个结点,构造它们同时为某个新节点的左右子结点,该新结点是他们共同的双亲结点,设定它的权值为其两个儿子结点的权值和。并将该父亲结点放入集合K。重复步骤(2)或(3);
(3)若集合K中仅剩余一个结点,该结点即为构造出的哈夫曼树数的根节点,所有构造得到的中间结点(即哈夫曼树上的非叶子节点)的权值和即为该哈夫曼树的带全路径和。
3.哈夫曼树的实现
为了方便求得集合K中权值最小的两个元素,我们需要使用堆数据结构。它可以以O(logn)的复杂度取得n个元素中的最小元素。
为了绕过对堆的实现,使用标准模板库中的相应的标准模板--优先队列
利用语句:priority_queue<int>Q;
建立一个保存元素为Int的堆Q,默认为大顶堆,即从堆顶取得的元素为整个堆中的最大的元素。
在哈夫曼树中,需要取得最小的元素,定义一个小顶堆:
priority_queue<int,vector<int>,greater<int>>Q;
关于堆的有关操作如下:
Q.push(x); //将元素x放入堆Q中
int a = Q.top(); //取出堆顶元素,取出后堆会自动调整为一个新的小顶堆
Q.pop(); //弹出堆顶元素,取出后堆会自动调整为一个新的小顶堆
预处理: #include<queue>
4.题目示例
题目分析:
(1)此题可通过建立哈夫曼树求得,非叶子结点的和即为总耗费体力
输出示例:
代码:
#include<queue>
#include<stdio.h>
using namespace std;
priority_queue<int,vector<int>,greater<int> > Q; // 建立一个小顶堆
int main(){
int n;
while(scanf("%d",&n)!=EOF&&n!=0){
while(!Q.empty()){
Q.pop();//清空堆中元素
}
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);//输入n个叶子结点权值
Q.push(x);//将权值放入堆中
}
int ans = 0;//保存答案
while(Q.size()>1){//当堆中元素大于1个
int a = Q.top();
Q.pop();
int b = Q.top();
Q.pop();//取出堆中两个最小的元素,它们为同一个结点的左右儿子,且该双亲结点的权值为它们的和
ans = ans+a+b;//该父亲结点必为非叶子结点
Q.push(a+b);
}
printf("%d\n",ans);
}
return 0;
}
代码分析:
(1)建立一个小顶堆
priority_queue<int,vector<int>,greater<int> > Q; // 建立一个小顶堆
(2)求非叶子结点的和
while(Q.size()>1){//当堆中元素大于1个
int a = Q.top();
Q.pop();
int b = Q.top();
Q.pop();//取出堆中两个最小的元素,它们为同一个结点的左右儿子,且该双亲结点的权值为它们的和
ans = ans+a+b;//该父亲结点必为非叶子结点
Q.push(a+b);
}
printf("%d\n",ans);
依次取出堆顶元素和堆栈顶元素,相加后再将和放入到堆中