王道计算机机试——哈夫曼树
题目描述
哈夫曼树,第一行输入一个数 n,表示叶结点的个数。需要用这些叶结点生 成哈夫曼树,根据哈夫曼树的概念,这些结点有权值,即 weight,题目需要输出 所有结点的值与权值的乘积之和。
1. 哈夫曼树求法
1)将所有结点放入集合 K。
2)若集合 K 中剩余结点大于 2 个,则取出其中权值最小的两个结点, 构造他 们同时为某个新节点的左右儿子,该新节点是他们共同的双亲结点,设定它的权 值为其两个儿子结点的权值和。并将该父亲结点放入集合 K。重复步骤 2 或 3。
3)若集合 K 中仅剩余一个结点, 该结点即为构造出的哈夫曼树数的根结点, 所有构造得到的中间结点 (即哈夫曼树上非叶子结点 )的权值和即为该哈夫曼树 的带权路径和。 为了方便快捷高效率的求得集合 K 中权值最小的两个元素,我们需要使用堆数 据结构。它可以以 O(logn)的复杂度取得 n 个元素中的最小元素。为了绕过对 堆的实现,我们使用标准模板库中的相应的标准模板 ——优先队列。
2.大顶堆与小顶堆
priority_queue<int> Q;
建立一个保存元素为 int 的堆 Q,但是请特别注意这样建立的堆其默认为大 顶堆,即我们从堆顶取得的元素为整个堆中最大的元素。而在求哈夫曼树中,我 们恰恰需要取得堆中最小的元素,于是我们使用如下语句定义一个小顶堆:
priority_queue<int, vector<int>, greater<int>> Q;//建立一个小顶堆
关于堆的有关操作如下:
Q.push(x); //将元素 x 放入堆 Q 中。
int a = Q.top();// 取出堆顶元素,即最小的元素保存在 a 中。
Q.pop();//弹出堆顶元素,取出后堆会自动调整为一个新的小顶堆。
3.代码
#include<queue>
#include<iostream>
using namespace std;
priority_queue<int, vector<int>, greater<int>> Q;//建立一个小顶堆
int main() {
int n;
while (cin >> n) {
while (Q.empty() == false) Q.pop();//清空堆中元素
for (int i = 1; i <= n; i++) {//输入n个叶子节点权值
int x;
cin >> x;
Q.push(x);
}
int ans = 0;//保存答案
while (Q.size() > 1) {//当堆中元素大于1个,最后剩余一个
int a = Q.top();
Q.pop();
int b = Q.top();
Q.pop();//取出堆中两个最小元素,它们同为一个节点的左右儿子,且该双亲结点的权值为它们的和
ans += a + b;//该父亲节点必为非叶子节点,所以累加其权值
Q.push(a + b);//将该双亲节点的权值放回堆中
}
cout << ans;
}
return 0;
}
4.时间复杂度
在使用了优先队列以后,求哈夫曼树的过程不仅时间复杂度降低许多( O (nlogn)),同时代码也轻便不少,所以使用数据结构堆来辅助求解哈夫曼树, 是求哈夫曼树的最佳选择。
来源:CSDN
作者:xyzxyzxyz1999
链接:https://blog.csdn.net/xyzxyzxyz1999/article/details/104747185