双亲孩子表示法
双亲孩子表示法就是在每一个结点中添加一个firstchild指针域用来存放孩子链表指针,而且每个节点会有一个存放双亲结点在顺序存储结构中的索引,这样可以更好的查找到每个结点的双亲。但是问题是这得提前给出该树的结点数量,并生成内存空间。
下面用C++类来实现一个简单的类,这个树的创建中,每个结点后面最少会跟着一个孩子结点,且最后一个孩子结点是空的。
主要难点是在基于当前树的某一个结点下插入另一棵树的实现,具体看代码
要实现的是创建A、B、C和D、E、F树,并在C结点插入D、E、F树:
代码:
#include <iostream>
#include <stdlib.h>
using namespace std;
#define MAX_SIZE 100
//双亲孩子表示法
class CTnode
{
public:
int child;
CTnode *next;
};
class CTbox
{
public:
int parent;
char data;
CTnode *firstchild;
};
class CTtree
{
public:
CTtree();
CTtree(int m);
CTtree(const CTtree &t);
int Treedepth();
int Value(char cur_e);//返回节点cur_e的双亲
// void Assige(char cur_e,char value);//给节点cur_e赋值valua
// CTbox * Parent(char cur_e);//返回非根节点cur_ed的双亲
char Leftchild(char cur_e);//返回左孩子
void Findsibling(char cur_e);//找兄弟
CTtree Insertchild(char cur_e,CTtree &c);//把树c插入cur_e节点中,成为cur_e的子树
void Deletechild(char cur_e,int i);//删除cur_e节点的第i个子树
friend ostream& operator<<(ostream& output,CTtree& p);
private:
CTbox *nodes;
int r,n;//根的位置和节点数
}
;
//实际上在g++编译器中,对于返回对象是类的函数,并不会领创建一个空间把对象复制进去
//为了节省消耗,编译器做了优化,不会调用自定义的复制构造函数也可以完成深复制,一次
//会发现,即便删除掉下面cout注释,也不会打印出任何字符
CTtree::CTtree(const CTtree &t)
{
CTnode *pnew,*qqnew;
nodes=new CTbox[t.n];//创建当前对象相同节点数量的CTbox数组
//cout<<"复制"<<endl;
for(int i=0;i<t.n;i++)
{
nodes[i].parent=t.nodes[i].parent;
nodes[i].data=t.nodes[i].data;
nodes[i].firstchild=new CTnode; //每一个节点后面都会跟至少一个CTnode结点
nodes[i].firstchild->child=-1;
nodes[i].firstchild->next=NULL;
pnew=nodes[i].firstchild;
qqnew=t.nodes[i].firstchild;
//对于当前对象 节点下有孩子的,进行转存复制
while(qqnew->next!=NULL&&qqnew->child!=-1)
{
pnew->child=qqnew->child;
// cout<<"复制:"<<nodes[i].data<<pnew->child<<endl;
pnew->next=new CTnode;//最后都会存在一个child=1 next=NULL的CTnode结点
pnew->next->child=-1;
pnew->next->next=NULL;
pnew=pnew->next;
qqnew=qqnew->next;
}
}
}
CTtree::CTtree(int m)
{
nodes=new CTbox[m];
n=m;
r=0;
}
//根的双亲是-1
CTtree::CTtree()
{
//char A='A';
char A='\0';
int p,num;
CTnode * pnew=NULL;
cout<<"输入节点数:";
cin>>num;
nodes=new CTbox[num];//这个数组的元素是对象,不是指针
for(int i=0;i<num;i++)
{
cout<<"输入节点的值(A--Z):";
cin>>A;
nodes[i].data=A;
nodes[i].firstchild=new CTnode;
nodes[i].firstchild->child=-1;
nodes[i].firstchild->next=NULL;
cout<<"输入节点的双亲位置下标:";
cin>>p;
nodes[i].parent=p;
//如果双亲值不是-1,且双亲值必比当前插入结点数小,说明该结点其双亲已经存在,那么就
//为它的双亲创建孩子节点(链表)
if(p<i&&p!=-1)
{
pnew=nodes[p].firstchild;
while(pnew->next!=NULL&&pnew->child!=-1)
{
pnew=pnew->next;
}
pnew->next=new CTnode;
//pnew=pnew->next;
pnew->child=i;
pnew->next->child=-1;
pnew->next->next=NULL;
}
}
r=0;
n=num;
}
//重载输出操作符
//按输入节点顺序打印,并在对应节点下输出其孩子节点data 样式如‘->B’
ostream& operator<<(ostream& output,CTtree& p)
{
CTbox *q;
CTnode *pnew;
bool childs=false;
for(int i=0;i<p.n;i++)
{
q=&(p.nodes[i]);
cout<<q->data<<' '<<q->parent<<endl;
pnew=q->firstchild;
while(pnew->next!=NULL&&pnew->child!=-1)
{
childs=true;
cout<<q->data<<"->"<<p.nodes[pnew->child].data<<' ';
pnew=pnew->next;
}
if(childs) //如果该节点有孩子,则换行,方便下一个节点打印,纯属是为了格式更好看
{
cout<<endl;
childs=false;
}
}
return output;
}
//计算当前树的深度
int CTtree::Treedepth()
{
int m=0,c=0;
int i=0;
for(;i<n;)
{
while(i<n&&c==nodes[i].parent)
{
i++;
}
if(i<n)
{
c=nodes[i].parent;
if(nodes[nodes[i].parent].parent!=nodes[nodes[i-1].parent].parent)
m++;
}
else
{
if(c!=nodes[i-1].parent)
{
if(nodes[nodes[i].parent].parent!=nodes[nodes[i-1].parent].parent)
m++;
}
}
}
return m;
}
int CTtree::Value(char cur_e)
{
int i=0;
while(nodes[i].data!=cur_e&&i<n)
i++;
if(i>=n)
return -2;
else
return nodes[i].parent;
}
char CTtree::Leftchild(char cur_e)
{
int i=0,m=0;
while(nodes[i].data!=cur_e&&i<n)
i++;
if(i>=n)
return '\0';
m=i; //记录下该节点索引,第几个节点
while(nodes[m].parent!=i&&m<n) //它的孩子的索引肯定比它自己索引要大,因此继续往下找以它为双亲的第一个结点就是它的左孩子
m++;
if(i>=n)
return '\0';
else
return nodes[m].data;
}
//
void CTtree::Findsibling(char cur_e)
{
int i=0,m=0;
CTnode * pnew;
while(nodes[i].data!=cur_e&&i<n)
i++;
if(i>=n)
return;
else if(i==0)
{
cout<<"根无兄弟节点"<<endl;
return;
}
pnew=nodes[nodes[i].parent].firstchild;//得到该节点双亲结点
//对这双亲的孩子进行打印,但是不打印自身
while(pnew!=NULL)
{
if(nodes[pnew->child].data!=nodes[i].data)
cout<<nodes[pnew->child].data<<' ';
pnew=pnew->next;
}
cout<<endl;
}
//需要添加一个复制构造函数
//调试的时候遇到运行过程中会把一些变量值改变,是因为在CTtree pnew(m)的构造函数中的参数变量传递的时候搞错了
//导致创建new的空间指针出现指向错误,以致与改变了一些参数值
CTtree CTtree::Insertchild(char cur_e,CTtree &c)
{
int m=n+c.n; //计算树c和当前树的节点数量
int i=0,j=0,k=-1;
CTtree pnew(m); //创建新的空树
CTnode *pnode,*qqnode;
//对当前对象进行了转存
for(;i<n;i++)
{
pnew.nodes[i].parent=nodes[i].parent;
pnew.nodes[i].data=nodes[i].data;
if(nodes[i].data==cur_e)//在对应节点进行插入,就需要把该结点的序号保存下来 存到k中
k=i;
pnew.nodes[i].firstchild=new CTnode;
pnew.nodes[i].firstchild->child=-1;
pnew.nodes[i].firstchild->next=NULL;
qqnode=pnew.nodes[i].firstchild;
pnode=nodes[i].firstchild;
while(pnode->next!=NULL&&pnode->child!=-1)
{
qqnode->child=pnode->child;
qqnode->next=new CTnode;
qqnode->next->child=-1;
qqnode->next->next=NULL;
qqnode=qqnode->next;
pnode=pnode->next;
}
}
//if(k==-1)
// exit(1);
//下一步是对c树进行插入转存
qqnode=pnew.nodes[k].firstchild;//得到要第k结点的孩子链表指针
while(qqnode->next!=NULL&&qqnode->child!=-1)
qqnode=qqnode->next;
qqnode->child=i; //k结点的下一个孩子肯定是c树的根结点,也就是第i个元素(i=当前树的n)
qqnode->next=new CTnode;
qqnode->next->child=-1;
qqnode->next->next=NULL;
for(;i<m&&j<c.n;j++,i++)
{
if(j==0)
{
pnew.nodes[i].parent=k;//c树的根节点双亲肯定是k结点
}
else
pnew.nodes[i].parent=n+c.nodes[j].parent;//c树的非根节点的双亲也就是在原来的基础上加上n(当前树的结点数量)
pnew.nodes[i].data=c.nodes[j].data;
pnew.nodes[i].firstchild=new CTnode;
pnew.nodes[i].firstchild->child=-1;
pnew.nodes[i].firstchild->next=NULL;
qqnode=pnew.nodes[i].firstchild;
pnode=c.nodes[j].firstchild;
while(pnode->next!=NULL&&pnode->child!=-1)
{
qqnode->child=pnode->child+n;
qqnode->next=new CTnode;
qqnode->next->child=-1;
qqnode->next->next=NULL;
qqnode=qqnode->next;
pnode=pnode->next;
}
}
return pnew;
}
/
int main()
{
CTtree tree1,tree3;
CTtree tree2(6);
cout<<tree1<<endl;
cout<<"深度值:"<<tree1.Treedepth()<<endl;
cout<<"双亲:"<<tree1.Value('B')<<endl;
cout<<"左孩子:"<<tree1.Leftchild('B')<<endl;
cout<<"兄弟:";
tree1.Findsibling('B');
cout<<"tree3"<<endl;
cout<<tree3<<endl;
cout<<"深度值:"<<tree3.Treedepth()<<endl;
cout<<"双亲:"<<tree3.Value('E')<<endl;
cout<<"左孩子:"<<tree3.Leftchild('E')<<endl;
cout<<"兄弟:";
tree3.Findsibling('E');
tree2=tree1.Insertchild('C',tree3);
cout<<tree2<<endl;
return 0;
}