定义
给定$N$个待权叶子结点构造一棵二叉树, 若该树带权路径长度达到最小, 且权值较大结点离根较近, 称为哈夫曼树
构建
graph TB
A((A:12))
B((B:24))
C((C:35))
D((D:67))
E((E:46))
F((F:55))
每次从所有节点中选出最小权值节点与次小权值节点合并为新节点
重复操作至所有节点合并为一棵树
- 选择节点$A、B$合并, 产生节点$36$
graph TB
C((C:35))
D((D:67))
E((E:46))
F((F:55))
X(((36)))
X --> A((A:12))
X --> B((B:24))
- 选择节点$C$与节点$36$合并, 产生节点$71$
graph TB
C((C:35))
D((D:67))
E((E:46))
F((F:55))
Y(((71)))
X(((36)))
Y --> X
Y --> C((C:35))
X --> A((A:12))
X --> B((B:24))
- 选择节点$E、F$合并, 产生节点$101$
graph TB
C((C:35))
D((D:67))
E((E:46))
F((F:55))
Z(((101)))
Z --> E
Z --> F
Y(((71)))
X(((36)))
Y --> X
Y --> C((C:35))
X --> A((A:12))
X --> B((B:24))
- 选择节点$D$与节点$71$合并, 产生节点$138$
graph TB
C((C:35))
D((D:67))
E((E:46))
F((F:55))
Z(((101)))
Z --> E
Z --> F
W(((138)))
W --> D
W --> Y
Y(((71)))
X(((36)))
Y --> X
Y --> C((C:35))
X --> A((A:12))
X --> B((B:24))
- 合并节点$101$与$138$, 完成构建
对建好哈夫曼树, 所有节点左儿子编为 $0$, 右儿子编为 $1$, 从根节点走到叶子节点实现编码
graph TB
C((C:35))
D((D:67))
E((E:46))
F((F:55))
Z(((101)))
Z --0--> E
Z --1--> F
W(((138)))
W --0--> D
W --1--> Y
V(((239)))
V --0--> Z
V --1--> W
Y(((71)))
X(((36)))
Y --0--> X
Y --1--> C((C:35))
X --0--> A((A:12))
X --1--> B((B:24))
实现
#include <iostream>
#include <utility>
#include <vector>
#include <memory>
#include <queue>
#include <map>
template<typename NodeType, typename WeightType>
struct Node {
NodeType mName;
WeightType mFrequency;
std::shared_ptr<Node> mLeftChild;
std::shared_ptr<Node> mRightChild;
Node(NodeType name, WeightType freq, std::shared_ptr<Node> left, std::shared_ptr<Node> right) :
mName(name), mFrequency(freq), mLeftChild(std::move(left)), mRightChild(std::move(right)) {}
};
template<typename NodeType, typename WeightType>
class HuffmanTree {
public:
using NodePtr = std::shared_ptr<Node<NodeType, WeightType>>;
explicit HuffmanTree(std::map<NodeType, WeightType>& table) {
auto Compare = [](NodePtr node1, NodePtr node2) {return node1->mFrequency > node2->mFrequency; };
std::priority_queue<NodePtr, std::vector<NodePtr>, decltype(Compare)> minHeap(Compare);
for (auto it = table.begin(); it != table.end(); ++it) {
minHeap.push(std::make_shared<Node<NodeType, WeightType>>(it->first, it->second, nullptr, nullptr));
}
while (minHeap.size() > 1) {
NodePtr left = minHeap.top();
minHeap.pop();
NodePtr right = minHeap.top();
minHeap.pop();
minHeap.push(std::make_shared<Node<NodeType, WeightType>>('\0', left->mFrequency + right->mFrequency, left, right));
}
NodePtr root = minHeap.top();
BuildCodes(root, "");
}
~HuffmanTree() = default;
void BuildCodes(const NodePtr& node, const std::string& code) {
if (!node) {
return;
}
if (node->mLeftChild == nullptr && node->mRightChild == nullptr) {
mCodeTable[node->mName] = code;
}
BuildCodes(node->mLeftChild, code + "0");
BuildCodes(node->mRightChild, code + "1");
}
void PrintCodeTable() const {
for (auto it = mCodeTable.begin(); it != mCodeTable.end(); ++it) {
std::cout << it->first << ": " << it->second << std::endl;
}
}
private:
std::map<NodeType, std::string> mCodeTable;
};
int main() {
std::map<char, int> table = {
{'A', 12}, {'B', 24}, {'C', 35},
{'D', 67}, {'E', 46}, {'F', 55}
};
HuffmanTree<char, int> tree(table);
tree.PrintCodeTable();
return 0;
}