C++構造哈夫曼樹
引用一下百度百科裏面的構造哈夫曼樹的描述:
假設有n個權值,則構造出的哈夫曼樹有n個葉子結點。 n個權值分別設爲 w1、w2、…、wn,則哈夫曼樹的構造規則爲:
(1) 將w1、w2、…,wn看成是有n 棵樹的森林(每棵樹僅有一個結點);
(2) 在森林中選出兩個根結點的權值最小的樹合併,作爲一棵新樹的左、右子樹,且新樹的根結點權值爲其左、右子樹根結點權值之和;
(3)從森林中刪除選取的兩棵樹,並將新樹加入森林;
(4)重複(2)、(3)步,直到森林中只剩一棵樹爲止,該樹即爲所求得的哈夫曼樹。
我的代碼就是照着這個百度百科構造的哈夫曼樹,☺️☺️😆😆
其中加權路徑長度就是根到葉節點的距離乘以路徑長度。
集合:11, 1, 32, 13, 6, 19, 4, 14的加權路徑長度爲:256
我寫了兩個版本的代碼,如下所示。
- 第一個版本:使用優先級隊列,邊合併邊計算,隊列存的是Tree類型
#include<bits/stdc++.h>
using namespace std;
vector<int>v{11, 1, 32, 13, 6, 19, 4, 14};// 待構造數據
struct Tree
{
int weight;// 樹的權重
Tree* left;// 左子樹
Tree* right;// 右子樹
bool leaf;// 是否是葉節點
bool operator<(const Tree& that)const{
return weight > that.weight;// 優先級隊列的比較規則,每次選取權重最小的樹出隊
}
Tree(int weight, Tree* left, Tree* right, bool leaf):weight(weight), left(left), right(right), leaf(leaf){}
};
int main(){
priority_queue<Tree>PQ;
for(auto i: v){
PQ.push({i, NULL, NULL, true});// 將每個待構造的元素看成一個無孩子的樹
}
int sum = 0;
while(PQ.size() > 1){// 只要隊列中超過一棵樹,就要進行合併
Tree first = PQ.top();// 彈出選取兩個權值最小的樹進行合併,重新壓入隊列
PQ.pop();
Tree second = PQ.top();
PQ.pop();
int weight = first.weight + second.weight;
sum += weight;// 每合併一次,加權路徑長度就會增加合併之和,仔細理解一下即可。
PQ.push({weight, &first, &second, false});
}
cout << sum << endl;
}
- 第二個版本,先用優先級隊列合併產生一個哈夫曼樹,優先級隊列存的是樹節點指針,類型爲Tree*,但我沒有直接寫成Tree*,而是封裝到Node裏面了,因爲我要重載比較規則,如果直接寫成Tree*,這是個地址,那麼比較規則則會變成比較地址的大小。所以我封裝到Node裏,在Node數中實現了Tree*的比較規則。拿到哈夫曼樹根後,再進行廣度優先搜索,獲取每個葉節點的距離根的長度,然後相乘求和即可。
#include<bits/stdc++.h>
using namespace std;
vector<int>v{11, 1, 32, 13, 6, 19, 4, 14};
struct Tree
{
int weight;
Tree* left;
Tree* right;
bool leaf;
Tree(int weight, Tree* left, Tree* right, int leaf):weight(weight), left(left), right(right), leaf(leaf){}
};
struct Node
{
Tree* tree;
bool operator<(const Node& that)const{
return tree->weight > that.tree->weight;
}
Node(Tree* tree):tree(tree){}
};
int main(){
priority_queue<Node>Q;
for(auto i: v){
Q.push({new Tree(i, NULL, NULL, true)});
}
while(Q.size() > 1){// 合併,求哈夫曼樹
Tree* first = Q.top().tree;
Q.pop();
Tree* second = Q.top().tree;
Q.pop();
Tree* newTree = new Tree({first->weight + second->weight, first, second, false});
Q.push(newTree);
}
Tree* root = Q.top().tree;// root爲哈夫曼樹的樹根
queue<pair<int,Tree*>>Que;
Que.push({0, root});
int sum = 0;
while(Que.size()){// 廣度優先搜索
pair<int, Tree*>head = Que.front();
Que.pop();
if(head.second->leaf){
sum += head.first*head.second->weight;
}else{
Que.push({head.first+1, head.second->left});
Que.push({head.first+1, head.second->right});
}
}
cout << sum << endl;
}