C++赫夫曼树的原理和实现

赫夫曼树的定义

基本概念

(1)节点之间的路径:一个结点到另一个结点,所经过节点的结点序列。

(2)结点之间的路径长度:结点之间路径上的分支数(边),如汽车到下一站的路径长度为1。

(3)树的路径长度:从根结点到每个叶子结点的路径长度之和。

(4)带权路径: 路径上加上的实际意义。如汽车到下一站的距离我们叫做权值

(5)树的带权路经长度:每个叶子结点到根的路径长度*权值 之和,记作WPL。

(6)使二叉树的带权路径(WPL)最小的树 ,我们叫做哈夫曼树。

赫夫曼树的构造方法

  假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:

(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);

(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;

(3)从森林中删除选取的两棵树,并将新树加入森林;

(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。

如:对 下图中的六个带权叶子结点来构造一棵哈夫曼树,步骤如下:
image

赫夫曼编码

定义

规定赫夫曼树的左分支代表0,右分支代表1,则从根结点到叶子结点所经过到路径分支组成到0和1到序列便为该结点对应字符到编码。

赫夫曼树的实现

实现思路

(1).遍历整个字符串,计算出每个字符的出现频率

(2).将所有的字符的频率作为权值存在赫夫曼树结构体中

(3).从所有的赫夫曼树挑选两个权重最小的结点并组成新结点,并标记这两个使用过的结点

(4).重复(3)直到所有结点都已经加入到赫夫曼树中

(5).根据左子树为0,右子树为1,从叶子结点反向计算出赫夫曼编码

(6).注意!!赫夫曼编码并不是唯一的,会根据结点在赫夫曼树中位置不同而改变

实现代码

赫夫曼树的存储结构
//int占4个字节,最大为2147483647
#define MAX 2147483647  (全局变量)

//代表赫夫曼树结点的结构体
struct HTNode
{
    int weight;
    int parent;
    int lchild, rchild;
    HTNode(int val = 0) : weight(val)
    {
        parent = 0;
        lchild = -1;
        rchild = -1;
    }
};
存储字符和其出现频率的结构体
struct LetterFre
{
    char ch;   //存储字符
    int count; //出现的频率
};
统计字符串中字符频率
//计算每个字符出现的频率
vector<LetterFre> CntFrequenceOfLetter(string str)
{
    vector<LetterFre> InfoList; //存储字符频率信息
    if (str.size() == 0)
        return InfoList;
    sort(str.begin(), str.end());
    char s = str[0];
    int count = 0;
    for (int i = 0; i < str.size(); i++)
    {
        if (str[i] != s)
        {
            //将统计的信息存入结构体
            LetterFre letter;
            letter.ch = s;
            letter.count = count;
            InfoList.push_back(letter);
            //开始统计下一个字符
            s = str[i];
            count = 1;
        }
        else
        {
            count++;
        }
        //如果为最后一个字符
        if (i == str.size() - 1)
        {
            //将统计的信息存入结构体
            LetterFre letter;
            letter.ch = s;
            letter.count = count;
            InfoList.push_back(letter);
        }
    }
    return InfoList;
}
从结点中找到权重最小的两个结点的索引
//从森林中选择权重最小的两棵树
void SelectTree(vector<HTNode> *T, int *s1, int *s2)
{
    int min = MAX;
    //选出第一小的
    for (int i = 0; i < T->size(); i++)
    {
        if ((*T)[i].parent == 0 && (*T)[i].weight <= min)
        {
            min = (*T)[i].weight;
            *s1 = i;
        }
    }
    min = MAX;  //MAX为全局变量
    //选出第二小的
    for (int i = 0; i < T->size(); i++)
    {
        if ((*T)[i].parent == 0 && (*T)[i].weight < min && *s1 != i)
        {
            min = (*T)[i].weight;
            *s2 = i;
        }
    }
}
构建赫夫曼树
//构造赫夫曼树
vector<HTNode> CrtHuffmanTree(vector<LetterFre> InfoList)
{
    vector<HTNode> HTNodeList(InfoList.size());
    if (HTNodeList.size() == 0)
        return HTNodeList;
    else
    {
        for (int i = 0; i < InfoList.size(); i++)
        {
            HTNodeList[i].weight = InfoList[i].count;
        }

        for (int j = InfoList.size(); j < 2 * InfoList.size() - 1; j++)
        {
            int s1 = 0, s2 = 0;
            //找到森林中权重最小的树
            SelectTree(&HTNodeList, &s1, &s2);
            //cout << s1 << " " << s2 << endl;
            //合并成新结点
            HTNode NewNode(HTNodeList[s1].weight + HTNodeList[s2].weight);
            NewNode.lchild = s1;
            NewNode.rchild = s2;
            HTNodeList[s1].parent = HTNodeList.size();
            HTNodeList[s2].parent = HTNodeList.size();
            HTNodeList.push_back(NewNode);
        }
    }
    return HTNodeList;
}
生成赫夫曼编码

void CrtHuffmanCode(vector<LetterFre> list1, vector<HTNode> list2)
{
    vector<int> code;
    for (auto i = 0; i < list1.size(); i++)
    {
        cout << list1[i].ch << ": ";
        auto crr = i;
        while (list2[crr].parent != 0)
        {
            auto parent = list2[crr].parent;
            if (crr == list2[parent].lchild)
                code.push_back(0);
            else if (crr == list2[parent].rchild)
                code.push_back(1);
            crr = list2[crr].parent;
        }
        //反向遍历容器
        for (auto it = code.rbegin(); it != code.rend(); ++it)
        {
            cout << *it << " ";
        }
        cout << endl;
        code.clear();
    }
}
实例
int main()
{
    string s = "AAABBBBCCCCCCCCCCDDDDDDDDEEEEEEFFFFF";
    auto list = CntFrequenceOfLetter(s);
    auto TreeList = CrtHuffmanTree(list);
    CrtHuffmanCode(list, TreeList);

    /*输出为
        A: 0 0 0 
        B: 0 0 1 
        C: 1 0 
        D: 0 1 
        E: 1 1 1 
        F: 1 1 0 
    */
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值