构造哈夫曼树,并求加权路径长度 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
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
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;
}

你可能感兴趣的:(数据结构,树)