导读
本篇文章将会介绍树的三种存储方式(双亲表示(顺序)、孩子表示(顺序+链式)、孩子兄弟表示法(链式))、二叉树和森林的转换、树和森林的遍历,并会给出完整的伪代码供读者参考
树的三种表示方法
对于二叉树,我们最常用的是二叉链表,但对于普通的树,每一个结点可以有多个孩子,在此有三章方法去表示,下面将一一介绍:
1.双亲表示法:
typedef struct PTNode{
char data;
int parents;
}PTNode;
typedef struct{
PTNode nodes[MAXSIZE];
int n;//结点数
}*PTree;
这种表示方法实际上就是一个结构体数组,每一个元素中包含了两个成员,一个是char型,存储数据内容,一个是int型,保存双亲的序号,如上图所示。
2.孩子表示法
孩子表示法是一种顺序存储与链式存储相结合的存储方式,如下图所示:
首先我们定义孩子结点CTNode:
typedef struct CTNode{
int child;
struct CTNode* next;
}CTNode; //孩子结点
再定义每一个元素的“块”:
typedef struct{
char data;
CTNode *firstchild;
}CTBox;
最后定义整个树:
typedef struct{
CTBox nodes[MAXSIZE];
int n,r;
}CTree;
3.孩子兄弟表示法:
typedef struct CSTNode{
char data;
struct CSTNode *firstchild,*nextsibling;
}CSTNode,*CSTree;
二叉树和森林转换
森林就是一个及一个以上的树组成的集合,这些树不一定是二叉树
森林转换成二叉树步骤:
1.把森林中每一棵树转换为二叉树
2.将这几棵树的头结点依次相连,右边相邻的为左边的右子树
举例:
二叉树到森林就是一个逆过程
树和森林的遍历
对于树的遍历,仅分为两种,先根和后根遍历
以先根为例讲解(后根自己类比)
先根就是先访问根结点,然后依次对子树访问:
比如,对于:
a
b c d
e f g h i
j k
的先根遍历为a b e j f c g k d h i
实现思路最简单的还是使用递归
大体思路如下:
void PreOrder(Tree T)
{
if(!T)
{
visit(T);
while(T还有子树)
PreOrder(T);
}
}
至于子树的寻找,与存储方式有关,在这里面,当然是孩子链表方法比较简单,但下面我将以孩子兄弟表示为例,分享一下我的思路:
首先我想附设一个函数
CSTree nextchild(int i,CSTree T)
这个函数返回结点T的子树,i表示第几棵子树,完整代码如下:
void PreOrder(CSTree T)
{
int i=1;
if(!T)
{
visit(T);
while(nextchild(i,T))
{
PreOrder(nextchild(i,T));
i++;
}
}
}
CSTree nextchild(int i,CSTree T)
{
if(i==1)
return T->firstchild;
T=T->firstchild;
while(i-1)
{
T=T->nextsibling;
i--;
}
return T;
}
最后看森林遍历,森林分成三个部分:
第一棵树的根节点
第一棵树的子树
除第一棵树的其它树
由此分成了先序和中序遍历
先序:
先访问第一个树根节点再第一棵树的子树再除第一棵树的其它树
相当于对每一棵树先序遍历
中序
先访问第一棵树的子树再第一棵树的根节点再除第一棵树的其它树
相当于对每一棵树后序遍历