哈弗曼树:又称最优二叉树,是带权路径长度最短的树。
哈夫曼编码:是一种前缀编码,即同一字符集中任何一个字符的编码都不是另外一个字符编码的前缀(最左子串)。
在哈弗曼树中,若用‘0’表示左子树,‘1’表示右子树,那么每当从根遍历到一个叶子节点时都会形成一个01串,即该叶子节点的编码,由于各个叶子节点已经是树的最末梢了,因此他们之间的编码不会有包含关系,这样就生成了前缀编码集。
那么,如何构建哈弗曼树呢?
首先,我们需要一串字符,并且得到其中每个字符出现的频次(作为权值),接着我们将这些权值放在一个vector中,并将其从小到大进行排序,之后取前两个(权值最小的两个)作为叶子节点构建一棵二叉树,将权值相加赋给新生成的根节点,之后将根节点加入到vector中,重复以上操作,直到vector中仅剩一个数据为止,该数据则是哈弗曼树最终的根节点。代码实现如下:
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
using namespace std;
typedef struct a
{
char key = '\0'; //字符
int weight = 0; //权值(出现频次)
struct a *LeftChild = nullptr;
struct a *RightChild = nullptr;
}Node;
bool cmp(Node *data1,Node *data2)
{
return data1 -> weight < data2 -> weight;
}
class HuffManTree
{
public:
Node *root;
explicit HuffManTree(map<char,int> d) //传入一个map,其中存储了所有的字符及其出现的频次
{
vector<Node *> weight_order;
// 遍历map,将其中的字符以及权值存储到各个叶子节点(node)中,将这些节点放在一个vector里面
for(auto iter : d)
{
auto tmp = new Node;
tmp -> key = iter.first;
tmp -> weight = iter.second;
weight_order.push_back(tmp);
}
// 根据先前的思想,我们至少应该同时获取两个节点的信息才能构建哈弗曼树,那么假如传入只有一个节点,就要进行特殊化处理
if(weight_order.size() == 1)
{
auto tmp = new Node;
auto t = new Node;
tmp -> weight = weight_order[0] -> weight;
Node_Copy(t,weight_order[0]);
weight_order.erase(weight_order.begin());
tmp -> LeftChild = t;
tmp -> RightChild = nullptr;
weight_order.push_back(tmp);
}
//正常情况下,节点数大于等于2,此时则需要不断地排序,建树,删除旧的两个节点,加入一个新的节点,直到最终vector中仅剩余一个节点(根节点)
while(weight_order.size() != 1)
{
sort(weight_order.begin(),weight_order.end(),cmp); //排序
auto tmp =