对于二叉树来说,输入时非常easy的,但对于n叉树,尤其时情况时,就显得略先困难。特别的,对于树的不同存储方式之间的转化就显得不是那么的容易,本文就n阶树孩子兄弟法(链式存储)存储转换为孩子法(邻接表)提供一个可靠的方法。
1.结构体的定义:
邻接表由两部分组成:表头结点表和边表。
邻接表中每个单链表的第一个结点存放有关顶点的信息,把这一结点看成链表的表头,其余结点存放有关边的信息
(1)表头结点表:包括数据域和链域,数据域存储顶点(data)的名称,链域(first)用于指向链表中第一个结点(与顶点邻接的第一个顶点)
(2)边表:包括邻接点域(指示与顶点邻接的点在图中的位置,即数组下标)、数据域(存储和边相关的信息,如权值)、链域(指示与顶点邻接的下一条边的结点)。
在此处建立时,我们可以按层次建立:首先定义边表中节点:
struct ctnode {
int child;
struct ctnode* next;
};
随后建立单层头表和边表:
typedef struct cno2 {
int js;
char data;
struct ctnode* first;
}cbox;
最后,定义多层即可建立:
typedef struct ccno
{
cbox node[maxsize];
int n, t;
}haitree;
定义部分代码:
typedef struct cno {
char data;
struct cno* firstchild, * mbro;
}cnode,*ctree;
struct ctnode {
int child;
struct ctnode* next;
};
typedef struct cno2 {
int js;
char data;
struct ctnode* first;
}cbox;
typedef struct ccno
{
cbox node[maxsize];
int n, t;
}haitree;
2.树的变形(变为二叉树)
孩子兄弟表示法,采用的是链式存储结构
其存储的实现思想是:从树的根节点开始,依次用链表存储各个节点的孩子节点和兄弟节点
因此,该链表中的节点应包含以下 3 部分内容:
- 节点的值
- 指向孩子节点的指针
- 指向兄弟节点的指针
依据以上方法,可以将n叉树转化为二叉树。
存储结构:
typedef struct cno {
char data;
struct cno* firstchild, * mbro;
}cnode,*ctree;
以下为孩子兄弟法代码实现:
ctree creat()
{
ctree p;
char x;
cin >> x;
if (x == '#')
return NULL;
else
{
p = (cnode*)malloc(sizeof(cnode));
p->data = x;
p->firstchild = creat();
p->mbro = creat();
}
return p;
}
3.n叉树先根和后根遍历
(1)、先根遍历:若树不为空,则先访问根,然后依次先根遍历各棵子树。
(2)、后根遍历:若树不为空,则先依次后根遍历各棵子树,然后再访问根结点。
void middletrave(ctree t)
{
if (t == NULL)
return;
else
{
cout << t->data << " ";
middletrave(t->firstchild);
middletrave(t->mbro);
}
}
void middletrave(ctree t)
{
if (t == NULL)
return;
else
{
middletrave(t->firstchild);
cout << t->data << " ";
middletrave(t->mbro);
}
}
4.孩子兄弟法转化为孩子法(邻接表)
(1). 表头节点对应值的填入:
对于邻接表表头节点,就为树中出现的所有字符,因此,可以在遍历书的时候,依次建立 表头节点。
(为了偷懒,就直接加入到上面的先序遍历中)
haitree ht;
void firsttrave(ctree t)
{
if (t == NULL)
return;
else
{
ht.node[ht.n].data = t->data;
ht.node[ht.n].js++;
ht.node[ht.n].first = (ctnode*)malloc(sizeof(ctnode));
ht.node[ht.n].first->child = -1;
ht.node[ht.n].first->next = NULL;
ht.n++;
cout << t->data << " ";
firsttrave(t->firstchild);
firsttrave(t->mbro);
}
}
其中ht定义为全局变量。
(2).生成对应的邻接表数据:
采用递归的方式扫描一个节点处理一个。
在将节点值对应到具体的头节点时:
(a).若左孩子不为NULL的(在原本的树结构中存在子节点)可以采用x1和x2记录该节点和该节点的左节点(第一个孩子),将两个和邻接表头节点对应,得到对应的节点号xh1和xh2,生成 节点后接入头节点。
(b).若有孩子不为NULL(该点和右子点为兄弟关系),因此该点的父节点和右子点为同一个个,只需找到右子点对应的节点号,生成节点后接入。
void bian(ctree t)
{
if (t->firstchild == NULL && t->mbro == NULL)
return;
char x1,x2,x3;
int xh1,xh2,xh3;
if (t->firstchild != NULL)
{
x1 = t->data;
x2 = t->firstchild->data;
for (int i = 0; i < ht.n; i++)
{
if (ht.node[i].data == x1)
{
xh1 = i;
}
if (ht.node[i].data == x2)
{
xh2 = i;
}
}
ctnode *p,*q;
p = (ctnode*)malloc(sizeof(ctnode));
p->child = xh2;
p->next = NULL;
q = ht.node[xh1].first;
if (q->next== NULL)
{
q->next = p;
}
else
{
while (q ->next!= NULL)
{
q = q->next;
}
q->next = p;
}
}
if (t->mbro != NULL)
{
x1 = t->data;
x3= t->mbro->data;
for (int i = 0; i < ht.n; i++)
{
if (ht.node[i].data == x1)
{
xh1 = i;
}
if (ht.node[i].data == x3)
{
xh3 = i;
}
}
ctnode* p, * q;
p = (ctnode*)malloc(sizeof(ctnode));
p->child = xh3;
p->next = NULL;
q = ht.node[xh1].first;
if (q->next == NULL)
{
q->next = p;
}
else
{
while (q->next != NULL)
{
q = q->next;
}
q->next = p;
}
}
if(t->firstchild!=NULL)
bian(t->firstchild);
if(t->mbro!=NULL)
bian(t->mbro);
}
5.输出邻接表结构
void ctrave(cbox p)
{
ctnode *q;
q = p.first;
q = q->next;
while (q != NULL)
{
cout << q->child << " ";
q = q->next;
}
}
通过以上的方法,我们可以将孩子兄弟法(链表)轻松的转化为孩子法(邻接表),并将其结构输出。
以下是源代码:
#include<bits/stdc++.h>
using namespace std;
#define maxsize 100
typedef struct cno {
char data;
struct cno* firstchild, * mbro;
}cnode,*ctree;
struct ctnode {
int child;
struct ctnode* next;
};
typedef struct cno2 {
int js;
char data;
struct ctnode* first;
}cbox;
typedef struct ccno
{
cbox node[maxsize];
int n, t;
}haitree;
haitree ht;
ctree creat()
{
ctree p;
char x;
cin >> x;
if (x == '#')
return NULL;
else
{
p = (cnode*)malloc(sizeof(cnode));
p->data = x;
p->firstchild = creat();
p->mbro = creat();
}
return p;
}
void firsttrave(ctree t)
{
if (t == NULL)
return;
else
{
ht.node[ht.n].data = t->data;
ht.node[ht.n].js++;
ht.node[ht.n].first = (ctnode*)malloc(sizeof(ctnode));
ht.node[ht.n].first->child = -1;
ht.node[ht.n].first->next = NULL;
ht.n++;
cout << t->data << " ";
firsttrave(t->firstchild);
firsttrave(t->mbro);
}
}
void middletrave(ctree t)
{
if (t == NULL)
return;
else
{
middletrave(t->firstchild);
cout << t->data << " ";
middletrave(t->mbro);
}
}
void bian(ctree t)
{
if (t->firstchild == NULL && t->mbro == NULL)
return;
char x1,x2,x3;
int xh1,xh2,xh3;
if (t->firstchild != NULL)
{
x1 = t->data;
x2 = t->firstchild->data;
for (int i = 0; i < ht.n; i++)
{
if (ht.node[i].data == x1)
{
xh1 = i;
}
if (ht.node[i].data == x2)
{
xh2 = i;
}
}
ctnode *p,*q;
p = (ctnode*)malloc(sizeof(ctnode));
p->child = xh2;
p->next = NULL;
q = ht.node[xh1].first;
if (q->next== NULL)
{
q->next = p;
}
else
{
while (q ->next!= NULL)
{
q = q->next;
}
q->next = p;
}
}
if (t->mbro != NULL)
{
x1 = t->data;
x3= t->mbro->data;
for (int i = 0; i < ht.n; i++)
{
if (ht.node[i].data == x1)
{
xh1 = i;
}
if (ht.node[i].data == x3)
{
xh3 = i;
}
}
ctnode* p, * q;
p = (ctnode*)malloc(sizeof(ctnode));
p->child = xh3;
p->next = NULL;
q = ht.node[xh1].first;
if (q->next == NULL)
{
q->next = p;
}
else
{
while (q->next != NULL)
{
q = q->next;
}
q->next = p;
}
}
if(t->firstchild!=NULL)
bian(t->firstchild);
if(t->mbro!=NULL)
bian(t->mbro);
}
void ctrave(cbox p)
{
ctnode *q;
q = p.first;
q = q->next;
while (q != NULL)
{
cout << q->child << " ";
q = q->next;
}
}
int main()
{
ctree t;
ht.node->js = 0;
ht.n = 0;
cout << "此方法用于树(n叉树,n>=2)。输入时用树和二叉树关系转化为二叉树" << endl;
t = creat();
cout << "先根遍历:";
firsttrave(t);
cout << endl;
cout << "后根遍历:" << endl;
middletrave(t);
cout << endl;
bian(t);
cout << "孩子兄弟表示法(邻接表)"<<endl;
for (int i = 0; i < ht.n; i++)
{
cout <<i<<" "<< ht.node[i].data << " ";
ctrave(ht.node[i]);
cout << endl;
}
}