赫夫曼编码

来源:互联网 发布:阿里云测试培训 编辑:程序博客网 时间:2024/06/05 03:34

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

//最优二叉树的左右子树可以互换,不影响树的带权路径长度
//最优二叉树的节点数是叶子的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;            }        } };