HuffMan树的(c++实现)by 55kg

本文介绍了使用C++实现Huffman树的过程,首先通过创建根节点并存储在vector中,然后利用排序和节点合并的方式逐步构建Huffman树。具体操作包括自定义比较函数或重载运算符<,对节点进行从小到大的排序,每次选取最小的两个节点生成新的节点,并更新vector,直到只剩下一个元素作为树的根节点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。
哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
在数据通信中,需要将传送的文字转换成二进制的字符串,用0,1码的不同排列来表示字符。例如,需传送的报文为“AFTER DATA EAR ARE ART AREA”,
这里用到的字符集为“A,E,R,T,F,D”,各字母出现的次数为{8,4,5,3,1,1}。现要求为这些字母设计编码。
要区别6个字母,最简单的二进制编码方式是等长编码,固定采用3位二进制,可分别用000、001、010、011、100、101对“A,E,R,T,F,D”进行编码发送,
当对方接收报文时再按照三位一分进行译码。显然编码的长度取决报文中不同字符的个数。若报文中可能出现26个不同字符,则固定编码长度为5。
然而,传送报文时总是希望总长度尽可能短。在实际应用中,各个字符的出现频度或使用次数是不相同的,如A、B、C的使用频率远远高于X、Y、Z,自然会想到设计编码时,
让使用频率高的用短码,使用频率低的用长码,以优化整个报文编码。
哈夫曼静态编码:它对需要编码的数据进行两遍扫描:第一遍统计原数据中各字符出现的频率,
利用得到的频率值创建哈夫曼树,并必须把树的信息保存起来,即把字符0-255(2^8=256)的频率值以2-4BYTES的长度顺序存储起来,
(用4Bytes的长度存储频率值,频率值的表示范围为0--2^32-1,这已足够表示大文件中字符出现的频率了)以便解压时创建同样的哈夫曼树进行解压;
第二遍则根据第一遍扫描得到的哈夫曼树进行编码,并把编码后得到的码字存储起来。
哈夫曼动态编码:动态哈夫曼编码使用一棵动态变化的哈夫曼树,对第t+1个字符的编码是根据原始数据中前t个字符得到的哈夫曼树来进行的,
编码和解码使用相同的初始哈夫曼树,每处理完一个字符,编码和解码使用相同的方法修改哈夫曼树,所以没有必要为解码而保存哈夫曼树的信息。
编码和解码一个字符所需的时间与该字符的编码长度成正比,所以动态哈夫曼编码可实时进行。

哈夫曼译码

在通信中,若将字符用哈夫曼编码形式发送出去,对方接收到编码后,将编码还原成字符的过程,称为哈夫曼译码。
1、路径和路径长度
在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
2、结点的权及带权路径长度
若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
3、树的带权路径长度
树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。 HuffMan树的构造方法:
假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:
(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
(3)从森林中删除选取的两棵树,并将新树加入森林;
(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。

创建图示:本次实现huffman树是先根据要要建树的所有关于叶节点的的信息先创建根节点,然后放到vector中,然后重载运算符<或者写一个比较的类这里面实现了静态成员函数的声明预定义,以及如果STL容器内的元素是模板类的指针的声明情况。把vector内的元素按照从小到大进行排序后,每次取最小的两个值,然后根据这两个节点创建一个新的节点,然后在vector中删除这两个最小值的节点,把新值加进来,然后再排一次顺序,再重复操作当容器内只剩一个元素时,这个元素就是这棵huffman树的根节点。二叉树的程序实现:

#include <QCoreApplication>
#include <iostream>
#include <vector>
using namespace std;
//继承自纯虚函数的子类,必须要重写父类的全部纯虚函数,否则编译会报这是一个抽象对象
//HuffMan树因为只有叶节点保存数据,所以叶节点与分支节点分开实现比较好
template<class T>
class HuffManNode
{
public:
    virtual int weight()=0;
    virtual bool Isleaf() const=0;
    virtual void setleft(HuffManNode<T>* Node)=0;
    virtual HuffManNode<T>* getleft() const=0;
    virtual void setright(HuffManNode<T>* Node)=0;
    virtual HuffManNode<T>* getright() const=0;
    virtual T getval()const=0;
};
template<class T>
class LeafNode:public HuffManNode<T>
{
private:
    int weg;
    T val;
public:
    LeafNode(int _weight,T _val):weg(_weight),val(_val)
    {}
    ~LeafNode()
    {}
    virtual int weight()
    {
        return weg;
    }
    virtual bool Isleaf() const;
    virtual void setleft(HuffManNode<T> *Node)
    {}
    virtual void setright(HuffManNode<T> *Node)
    {}
    virtual T getval()const
    {
        return val;
    }
    virtual HuffManNode<T>*getleft() const
    {
        return NULL;
    }
    virtual HuffManNode<T>*getright() const
    {
        return NULL;
    }
};
template<class T>
bool LeafNode<T>::Isleaf() const
{
    return true;
}
template<class T>
class IntenalNode:public HuffManNode<T>
{
private:
    HuffManNode<T> *lc;
    HuffManNode<T> *rc;
    int weg;
public:
    IntenalNode(HuffManNode<T>* _lc=NULL,HuffManNode<T>* _rc=NULL):lc(_lc),rc(_rc)
    {
        weg=lc->weight()+rc->weight();
    }
    virtual int weight()
    {
        return weg;
    }
    virtual bool Isleaf() const
    {
        return false;
    }
    virtual void setleft(HuffManNode<T> *Node);
    virtual void setright(HuffManNode<T> *Node);
    virtual HuffManNode<T>* getleft() const;
    virtual HuffManNode<T>* getright() const;
    virtual T getval()const
    {}
};
template<class T>
void IntenalNode<T>::setleft(HuffManNode<T> *Node)
{
    lc=Node;
}
template<class T>
void  IntenalNode<T>::setright(HuffManNode<T> *Node)
{
    rc=Node;
}
template<class T>
HuffManNode<T>* IntenalNode<T>::getleft() const
{
    return lc;
}
template<class T>
HuffManNode<T>* IntenalNode<T>::getright() const
{
    return rc;
}
template<class T>
class HuffTree
{
private:
    HuffManNode<T> *subroot;
    bool operator < (HuffTree<T>* x)
    {
        return subroot->weight()<x->weight();
    }
public:
    HuffTree(int _weg,T _val)
    {
        subroot=new LeafNode<T>(_weg,_val);
    }
    HuffTree(HuffTree<T> *_lc,HuffTree<T>* _rc)
    {
        subroot=new IntenalNode<T>(_lc->root(),_rc->root());
    }
    ~HuffTree()
    {}
    HuffManNode<T>* root()
    {
        return subroot;
    }
    int weight()
    {
        return subroot->weight();
    }
};
template<class T>
class HHcompare
{
public:
    static bool lt(HuffTree<T>* x,HuffTree<T>* y)
    {
        return x->weight()<y->weight();
    }
    static bool gt(HuffTree<T>* x,HuffTree<T>* y)
    {
        return x->weight()>y->weight();
    }
    static bool eq(HuffTree<T>* x,HuffTree<T>* y)
    {
        return x->weight()==y->weight();
    }
};
template <class T>
class HuffManTree
{
private:
    HuffTree<T>* subroot;
    vector<HuffTree<T>* > woods;
    vector<HuffTree<T>*> CreateHuffTreate(vector<HuffTree<T>*> &Barry)
    {
        //获取最前面的两个
        sort(Barry.begin(),Barry.end(),HHcompare<T>::lt);//之前先排序
        HuffTree<T> *ParNode=new HuffTree<T>(Barry.at(0),Barry.at(1));
        class vector<HuffTree<T>*>::iterator it_fir=Barry.begin();
        Barry.erase(it_fir);
        class  vector<HuffTree<T>* >::iterator it_sec=Barry.begin();
        Barry.erase(it_sec);
        Barry.push_back(ParNode);
        return Barry;
    }
public:
    HuffManTree(HuffTree<T>* _subroot=NULL):subroot(_subroot)
    {
        woods.empty();
    }
    ~HuffManTree()
    {}
    T findTpu(string length)
    {
        HuffManNode<T>*_root=subroot->root();
        return findT(_root,length);
    }

    void insertNode(int _weg,T _val)
    {
        HuffTree<T>* Node=new HuffTree<T>(_weg,_val);
        woods.push_back(Node);
    }
    void CreateTree()
    {
        vector<HuffTree<T>*> Barry=woods;
        while(Barry.size()>1)
        {
            CreateHuffTreate(Barry);
        }
        subroot=Barry.at(0);
    }
    T findT(HuffManNode<T>*&_root,string length)
    {
        int size=length.length();
        for(int i=0;i<size;i++)
        {     if(_root->Isleaf())
            {
                break;
            }
            char Num=length.at(i);
            if(Num=='1')
            {
                _root=_root->getleft();
            }
            else if(Num=='0')
            {
                _root=_root->getright();
            }

        }
        return _root->getval();
    }
};



int main(int argc, char *argv[])
{
    
    HuffManTree<char>* A=new HuffManTree<char>();
    A->insertNode(2,'A');
    A->insertNode(2,'B');
    A->insertNode(3,'C');
    A->insertNode(1,'D');
    A->insertNode(5,'E');
    A->CreateTree();//创建完成,遍历获取编码
    char l=A->findTpu("111");
    cout<<l<<endl;
    cout<<"ds";
   return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值