//赫夫曼编码:最优二叉树,是带权路径长度最短的二叉树; 根据结点的个数和权值不同,最优二叉树的形状也不同
//将每个带有权值的结点作为一颗仅有根结点 的二叉树,树的权值为节点的权值
//将其中两颗权值最小 的树组成一颗新二叉树,新树的权值为两棵树的权值和
//重复上述两个步骤
//最优二叉树的左右子树可以互换,不影响树的带权路径长度
//最优二叉树的节点数是叶子的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;
}
}
};