个人觉得,教材上实现哈夫曼树地方法太繁琐了,容易劝退。。
先贴代码
const int MAXSIZE = 5;//字符数目
int w[MAXSIZE + 1] = { 0,1,2,3,4,5 }; //保存abcde对应的权重
string s[MAXSIZE+1];//abcde对应的编码
char a[MAXSIZE + 1] = { '#','a','b','c','d','e' };//出现的字符
//节点结构
typedef struct HTNode{
int weight; //该节点的权值
char c; // 该节点对应的字符,默认为#
HTNode* lchild, *rchild; //左右子树
bool operator<(const HTNode& a) {
return weight < a.weight;
}
} * HuffmanTree;
//自定义比较规则
struct cmp {
bool operator()(const HuffmanTree& a, const HuffmanTree& b) {
return a->weight > b->weight;
}
};
//堆,优先队列
priority_queue <HuffmanTree,vector<HuffmanTree>,cmp> q;
//后序遍历哈夫曼树,获取字符编码
void back_for_each(HuffmanTree T, string code) {
if (T) {
back_for_each(T->lchild, code + "0");
back_for_each(T->rchild, code + "1");
if (T->c != '#') {
s[(T->c) - 'a' + 1] = code;
}
}
}
//构建哈夫曼树
void HufffmanCoding() {
//初始化节点
int m = 2 * MAXSIZE - 1;
HuffmanTree HT = new HTNode[m + 1]; // 不使用零号单元
HuffmanTree p=HT+1;
for (int i = 1; i <= MAXSIZE; ++i,++p) {
*p = { w[i], a[i] , 0, 0 }; //初始化每一个基本节点abcde
q.push(p);//放入优先队列中
}
//构建哈夫曼树
while (q.size() > 1) {
HuffmanTree s1 = q.top();
q.pop();
HuffmanTree s2 = q.top();
q.pop();
if (s1->weight < s2->weight) {
HuffmanTree ptr = new HTNode;
(*ptr) = { s1->weight + s2->weight,'#',s1,s2 };
q.push(ptr);
}
else {
HuffmanTree ptr = new HTNode;
(*ptr) = { s1->weight + s2->weight,'#',s1,s2 };
q.push(ptr);
}
}
HuffmanTree root = q.top();
//获取字符编码
back_for_each(root,"");
}
//编码
string encoding(const string &a) {
string temp="";
for (auto c : a) {
temp = temp + s[c - 'a' + 1];
}
return temp;
}
//解码
string decoding(const string& a) {
string temp = "", ans = "";
for (auto c : a) {
temp = temp + c;
int flag = 0;
int cnt=0;
for (auto c : s) {
if (cnt == 0) {
cnt++;
continue;
}
if (temp == c) {
ans += char('a' - 1 + cnt);
flag = 1;
}
cnt++;
}
if (flag)temp = "";
}
return ans;
}
int main() {
HufffmanCoding();
for (int i = 1; i <= 5; ++i) {
cout << char('a'+i-1) << "的编码为:" << s[i] << endl;
}
cout << "请输入待编码的字符串:";
string input;
cin >> input;
string ans = encoding(input);
cout <<"编码为:"<< ans << endl;
string ans2 = decoding(ans);
cout << "解码为: " << ans2 << endl;
return 0;
}
手动构建哈夫曼
首先,回想一下我们是如何构建哈夫曼树的:
1.设初始每一个节点都是一颗树(只有一个节点)
2.不断地从集合中挑两个权值最小的节点,合成一颗树,放回集合中;直至集合中只存在一个树
3.取顶部的节点作为根节点
OK,接下来就是将这个过程进行代码实现:D
代码实现
首先定义节点结构
//节点结构
typedef struct HTNode{
int weight; //该节点的权值
char c; // 该节点对应的字符,默认为#
HTNode* lchild, *rchild; //左右子树
bool operator<(const HTNode& a) {
return weight < a.weight;
}
} * HuffmanTree;
然后开始建树
下面是定义一个集合,可以自动排序(优先队列)
//自定义比较规则
struct cmp {
bool operator()(const HuffmanTree& a, const HuffmanTree& b) {
return a->weight > b->weight;
}
};
//堆,优先队列
priority_queue <HuffmanTree,vector<HuffmanTree>,cmp> q;
下面是不断选择、合并的过程
1.初始化节点
首先new出m+1个节点(HTNode)
按顺序给MAXSIZE个初始节点赋值(这里我从1号单元开始赋值,不用0号单元);
放入集合q中。
初始的节点,左右子树都为空
2.构建哈夫曼树
执行以下循环,直至q.size()==1:
- 1、取集合(优先队列)前两个节点
- 2、new一个新的节点,令其左右子树分别指向第一位和第二位
- 3、设置其权值为第一位和第二位的和
令根指针指向集合中的最后一个节点
//构建哈夫曼树
void HufffmanCoding() {
//初始化节点
int m = 2 * MAXSIZE - 1;
HuffmanTree HT = new HTNode[m + 1]; // 不使用零号单元
HuffmanTree p=HT+1;
for (int i = 1; i <= MAXSIZE; ++i,++p) {
*p = { w[i], a[i] , 0, 0 }; //初始化每一个基本节点abcde
q.push(p);//放入优先队列中
}
//构建哈夫曼树
while (q.size() > 1) {
HuffmanTree s1 = q.top();
q.pop();
HuffmanTree s2 = q.top();
q.pop();
if (s1->weight < s2->weight) {
HuffmanTree ptr = new HTNode;
(*ptr) = { s1->weight + s2->weight,'#',s1,s2 };
q.push(ptr);
}
else {
HuffmanTree ptr = new HTNode;
(*ptr) = { s1->weight + s2->weight,'#',s1,s2 };
q.push(ptr);
}
}
HuffmanTree root = q.top();
//获取字符编码
back_for_each(root,"");
}
以上,哈夫曼树的构建就完成了。
这里需要指出代码中的几个问题(我自己也没搞清楚):
- 在HTNode结构的代码中,重载<运算符是为了帮助优先队列排序,cmp()函数也是帮助优先队列排序,二者应该功能重复了,取一个即可。
- 在“构建哈夫曼树-初始化节点”中,我new了m=2*MAXSIZE+1个节点,但是在后面并没有用到。因为后面创建节点时,我又重新new了:D 故此处可以简化。
- 优先队列是真的香:)
- back_for_each()是后序遍历获取字符编码,其它的就不讲了。