#include<iostream>
#include<string>
using namespace std;
//哈夫曼树结构
typedef struct HFnode
{
int data; //字母权值
string leter; //对应字母
HFnode *lson, *rson; //左右子树
HFnode *next, *parent; //森林域和双亲域
}*HFptr;
//哈夫曼所有结点的字符集合
//用于后面的译码
typedef struct HFaggregate
{
string leter; //哈夫曼结点的字母值
HFnode *point; //指向对应哈夫曼结点
}*HFag;
//哈夫曼森林转为哈夫曼树的函数
void mergeHFT(HFptr &root, int size)
{
//n棵哈夫曼子树合并为一颗哈夫曼树
HFptr t1, t2, p, q, r;
for (int i = 0; i < size; i++)
{
r = new HFnode();
t1 = root->next;
t2 = t1->next;
t1->parent = t2->parent = r;
r->lson = t1;
r->rson = t2;
r->leter = "0";
root->next = t2->next;
p = root;
q = root->next;
//将新生点r有序插入森林域中
while (1)
{
if (q->data < r->data)
{
p = q;
q = q->next;
}
else
{
r->next = r;
p->next = r;
break;
}
}
}
p = root;
root = root->next;
delete p;
}
//创建哈夫曼森林的函数
//root——哈夫曼树的根, set——后面译码需要用到的字符集合size数据个数
void createHFT(HFptr &root, HFag &set, int size)
{
int num = 0;
string str;
HFptr p = nullptr;
//动态创建哈夫曼树与字母集合
p = root = new HFnode();
set = new HFaggregate[size];
//根的权重最大 0x7f7f为16进制的最大数
root->data = 0x7f7f;
cout << "请输入相应字符与权重(权重从小到大输入):" << endl;
//权重从小到大输入,若乱序,则需要进行排序后构造哈夫曼树
for (int i = 0; i < size; i++)
{
p->next = new HFnode();
p = p->next;
p->lson = p->rson = p->parent = nullptr;
cin >> str >> num;
//分别将str字母与对应的权重num赋值给哈夫曼树字结点 p 与 字母集合 set 中
//set[i]是set的一个元素 set是集合
p->leter = set[i].leter = str;
p->data = num;
//set同时还存储对应字母的哈夫曼树结点指针
set[i].point = p;
}
p->next = root;
//调用mergeHFT函数将哈夫曼森林合并成一棵树
//size-1 例如5个结点,合并次数4次就行了,故合并次数 = 结点 - 1
mergeHFT(root, size - 1);
}
//哈夫曼树译码
void decoding(HFptr root)
{
string str, x;
HFptr p = root;
cin >> str;
//对输入译码的字符串str逐个去编码
for (size_t i = 0; i <= str.size(); i++)
{
x = str[i];
//判断当前字符x是左子树还是右子树
if (x == "0")
{
//当前结点为叶子结点,则打印
if (p->lson == nullptr)
{
cout << p->leter;
//记得p要重新指向root下次查找还是进行
p = root;
//否则就找当前结点的左子树
p = p->lson;
}
}
else
{
//当前结点为叶子结点,则打印
if (p->lson == nullptr)
{
cout << p->leter;
p = root;
}
//否则就找当前结点的右子树
p = p->rson;
}
}
}
//编码路径
string encodingPath(HFptr root, HFptr p)
{
string x, y;
HFptr q = p->parent;
//由上向下记录路径
while (q != root)
{
if (q->lson != p)
{
x += "1";
}
else
{
x += "0";
}
p = q;
q = q->parent;
}
//判断最后一个结点是root的左子树还是右子树
if (root->lson != p)
{
x += "1";
}
else
{
x += "0";
}
//路径由于是下往上,故需要反转
for (int i = x.size() - 1; i != -1; i--)
{
y += x[i];
}
return y;
}
//哈夫曼树编码
void encoding(HFptr root, HFag set, int size)
{
string str, x;
cin >> str;
for (size_t i = 0; i < str.size(); i++)
{
x = str[i];
//遍历字符集里面的元素, 找到字母所对应的哈夫曼结点指针,再把对应的路径输出
//set集合最后一个元素的下标为size - 1, 由于set之前输入是权重从大到小输入,
//故权重越大出现的频率大的放到前面遍历,提高效率
for (int j = size - 1; j != -1; j--)
{
if (x == set[j].leter)
{
cout << encodingPath(root, set[j].point);
}
}
}
}
int main()
{
HFptr root = nullptr;
HFag set = nullptr;
int size = 0;
cout << "请输入字符个数:" << endl;
cin >> size;
createHFT(root, set, size);
cout << "请输入编码的字符串:" << endl;
encoding(root, set, size);
cout << endl << "请输入译码的字符串:" << endl;
decoding(root);
cout << endl;
system("pause");
return 0;
}
c++实现哈夫曼编码
最新推荐文章于 2023-08-27 22:12:56 发布