#include<iostream>
#include<queue>
#include<vector>
#include<string>
using namespace std;
//创建结点类
class Node {
public:
Node() {};
Node(string val, double weight) {
this->val = val;
this->weight = weight;
this->left = this->right = nullptr;
}
public:
double weight;//权值
string val;//结点存储的数据
Node *left;//左孩子结点
Node *right;//右孩子节点
Node *parent;//父节点
bool Isown;//是否为有效结点
};
//自定义优先队列里的排序规则
class cmp {
public:
cmp() {};
bool operator () (Node* &a, Node* &b) {
return a->weight > b->weight;
}
};
//哈夫曼树类
class HuffmanTree {
public:
//哈夫曼树的初始化
HuffmanTree(int n) {
this->num = n;//初始化哈夫曼树的有效结点个数
this->head = new Node;
this->head->left = nullptr;
this->head->right = nullptr;
this->head->parent = nullptr;
}
//创建哈夫曼树
void creatHuffmanTree() {
int i = 0;
while (i < this->num) {
cout << "请输入结点及其权值:";
Node *np = new Node;
cin >> np->val >> np->weight;
np->Isown = true;//有效结点记为true
this->pq.push(np);//将有效节点压入优先队列中
i++;
}
//开始先从队列中拿出两个最小的结点构成一棵树
Node *t = new Node;
t = pq.top();
pq.pop();
this->head->left = t;
t->parent = this->head;
t = pq.top();
this->head->right = t;
t->parent = this->head;
pq.pop();
while (!pq.empty()) {
//每次拿出一个未在树中最小的结点
t = pq.top();
//将原树的根结点变为一个叶子节点
Node* s = new Node;
s->weight = this->head->left->weight + this->head->right->weight;
s->left = this->head->left;
s->right = this->head->right;
s->left->parent = s->right->parent = s;
s->Isown = false;//将原树的根结点标记未false,表示不是有效结点
if (t->weight > s->weight) {
this->head->left = s;
this->head->right = t;
}
else {
this->head->left = t;
this->head->right = s;
}
s->parent = t->parent = this->head;
pq.pop();
}
//将哈夫曼树的根结点标记为false
this->head->Isown = false;
}
void getCode() {
//初始编码为0,并往左查找一次
string code = "0";
Node* t = new Node;
t = this->head->left;
//当所有有效结点的哈夫曼编码都生成完成后退出循环
while (this->code.size() < this->num) {
//如果当前结点是有效结点,则将此时的哈夫曼编码赋给当前结点
if (t->Isown) {
this->code.push_back(pair<string, string>(t->val, code));
}
//如果当前结点的左孩子不为空,则继续往左查找,并哈夫曼编码添加一位0
if (t->left != nullptr) {
t = t->left;
code += "0";
}
//如果当前结点的左孩子为空但右孩子结点不为空,则往右查找,并哈夫曼编码添加一位1
else if (t->right != nullptr) {
t = t->right;
code += "1";
}
//如果当前结点的左右孩子均为空,但自己是父节点的左孩子,则到父节点的右孩子接着查找,并去除最后一位哈夫曼编码再添加一位1
else if(t != t->parent->right){
t = t->parent->right;
code.pop_back();
code += "1";
}
//如果当前结点的左右孩子均为空且自己是父节点的右孩子,则回到父节点的父节点的右孩子,去除哈夫曼编码的最后两位再添加一位1
else {
t = t->parent->parent->right;
code.pop_back();
code.pop_back();
code += "1";
}
}
cout << "由以上结点生成的哈夫曼编码为:" << endl;
for (auto cd : this->code) {
cout << cd.first << ":" << cd.second << endl;
}
}
private:
Node* head;//哈夫曼树的根结点
int num;//哈夫曼树的有效节点数
priority_queue <Node*, vector<Node*>, cmp > pq;//存储有效节点的优先队列
vector< pair<string, string> > code;//存储每个有效节点的哈夫曼编码的键值对的向量
};
int main() {
int n;
cout << "请输入结点数:" << endl;
cin >> n;
HuffmanTree hfft(n);
hfft.creatHuffmanTree();
hfft.getCode();
system("pause");
return 0;
}