構造哈夫曼樹,並求加權路徑長度 C++(優先級隊列)

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

我寫了兩個版本的代碼,如下所示。

  1. 第一個版本:使用優先級隊列,邊合併邊計算,隊列存的是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;
}
  1. 第二個版本,先用優先級隊列合併產生一個哈夫曼樹,優先級隊列存的是樹節點指針,類型爲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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章