赫夫曼编码

//赫夫曼编码:最优二叉树,是带权路径长度最短的二叉树; 根据结点的个数和权值不同,最优二叉树的形状也不同
//将每个带有权值的结点作为一颗仅有根结点 的二叉树,树的权值为节点的权值
//将其中两颗权值最小 的树组成一颗新二叉树,新树的权值为两棵树的权值和
//重复上述两个步骤

//最优二叉树的左右子树可以互换,不影响树的带权路径长度
//最优二叉树的节点数是叶子的2倍-1;

//赫夫曼编码:设置初态——获取终态——从叶子到根逆序编码
这里写图片描述
首先创建赫夫曼树,N个叶子有2*n-1个结点的树,用顺序存储结构a[]存储;初态设置如上图(a)所示;parent=-1表示该结点还未被访问
接下来,从数组中选取两个weight最小的叶子结点构造新的结点,如a[6]:weight=3和a[0]:weight=5构成结点a[8]:weight=8;结点a[8]的左右孩子分别填充序号6、0;依次填充直至完成;
最后进行编码,若为左子树取0,右子树取1; 从叶子结点向根节点进行逆序编码;请仔细查看上图,发现最后一行,即根节点的parnet=-1,以次判断是否从叶子编码至根节点;
(详见严蔚敏de《数据结构》和高一凡的《数据结构算法和分析(STL版)》)

#include<vector>
template<class T> 
struct HTNode{
    T weight;
    int parent,lchild,rchild;
}; 

template<class T>
class HuffmanTree{
    private:
        vector<HTNode>> HT;  //树的顺序存储结构 
        int N;
        bool Make;

        void Select(int i,T&j1,T&j2) const{
            //返回前i个节点中权值最小和次小的两个树的结点序号
            int i,m;            
            for(j=0;j<i,HT[j].parent!=-1;j++); 
            j1=j;
            for(j=j1+1;j<i,HT[j].parent!=-1;j++);
            if(HT[j].weight<HT[j1].weight){
                j2=j1;
                j1=j;
            }else j2=j; // 确保j1<j2 

            //parent=-1;表示此叶子还未被访问 
            for(m=j+1;m<i;m++){
                if(HT[m].parent==-1 && HT[m].weight<HT[j1].weight){
                    j2=j1;
                    j1=m;
                }else if(HT[m].parent==-1&& HT[m].weight<HT[j2].weight)
                    j2=m;
            }

        }
    public:
        HuffmanTree(){
            Make=false; 
        } 
        void CreateHT(string FileName){
            ifstream fin(FileName.c_str());
            fin>>N;  //叶子个数
            if(N<=1) return;

            int i,s1,s2;
            HT.assign(2*N-1);  //赫夫曼树的结点总数;
            //初态:输入前N个叶子的权值,其余Parent、lchild、rchild全为-1; 
            for(i=0;i<N;i++){
                fin>>HT[i].weight; cout<<HT[i].weight<<" ";
                HT[i].parent=-1;
                HT[i].lchild=-1;
                HT[i].rchild=-1;}           
            cout<<endl;
            fin>>close();           
            //终态: 
            for(i=N;i<HT.size();i++){
                Select(i,s1,s2);
                HT[s1].parent=HT[s2].parent=i;
                HT[i].weight=HT[s1].weight+HT[s2].weight;
                HT[i].lchild=s1;
                HT[i].rchild=s2;
            }
            Make=true;  //建立赫夫曼树的标志 
        }
        //编码 
        void HuffmanCodingLeaf() const{
            if(Make){
                string *HC=new string[N];
                assert(HC!=NULL);
                cout<<"从叶子到根逆向求得赫夫曼编码:"<<endl;
                for(int i=0;i<N;i++){
                    for(int c=i,p=HT[i].parent; p>=0; c=p;p=HT[p].parent){
                        if(c==HT[p].lchild) HT[i].insert(0,"0");
                        else HC[i].insert(0,"1");
                        cout<<HC[i]<<endl;
                    }                   
                }
                delete []HC;
            }
        } 
        void HuffmanTree(){
            if(Make){
                string str="",*HC=new string[N];
                assert(HC!=NULL);
                vector<int> s(HT.size(),0);//访问标志012 

                int c=HT.size()-1;//根节点的序号 
                while(c>-1){
                    if(s[c]==0){//未被访问 
                        s[c]=1;
                        if(HT[c].lchild>-1){
                            c=HT[c].lchild;  str+='0'; 
                        } 
                        else{
                            HC[c]=str;
                            c=HT[c].parent;//向根方向后退一步 
                            str.erase(str.size()-1,1);
                        }
                    }
                    else if(s[c]==1){
                        s[c]=2;
                        c=HT[c].rchild;
                        str+='1';
                    }
                    else{
                        c=HT[c].parent;
                        if(c>-1) str.erase(str.size()-1,1);
                    }
                } 
                cout<<"无栈非递归从根到叶子求得的赫夫曼编码:"<<endl;
                for(int i=0;i<N;i++)
                    cout<<HC[i]<<endl;
                delete []HC;
            }
        } 
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值