绪论
我依次介绍先序、后序、层序、广义表四种形式创建二叉树的方法,二叉树样例均使用下方样例来进行举例讲解.
另外提一下为啥不介绍中序创建二叉树的方法?原因是中序创建二叉树是不存在的,因为中序无法确定一棵唯一的二叉树,即可能有两棵或多棵不同的二叉树,它们的中序是一样的
1.二叉树样例
2.二叉树结构定义
typedef char TElemType; //树的数据的类型
typedef struct BiTNode //树节点
{
TElemType data; //数据成员
struct BiTNode *lchild; //左子树指针
struct BiTNode *rchild; //右子树指针
}BiTNode,*BiTree; //节点,节点指针
一.先序创建二叉树
1.思路分析:
先序创建可以使用非递归,也可以使用递归,递归创建会简单很多,所以这里使用递归的方法.我们先序创建二叉树的顺序是 根节点->左子树->右子树,以输入’#'为创建空树标志
先序输入:ABC##DE#G##F###
算法简单就不过多陈述,直接看代码
2.代码实现
//先序创建二叉树
void PreCreateBiTree(BiTree& T)
{
TElemType elem;
scanf("%c",&elem); //输入一个元素
if(elem=='#') //如果是输入'#'则分叉结束
T=NULL; //分叉结束,赋值为空
else
{
T=(BiTree)malloc(sizeof(BiTNode)); //给节点分配内存
T->data=elem; //父节点存入输入的数据
PreCreateBiTree(T->lchild); //先序创建左子树
PreCreateBiTree(T->rchild); //先序创建右子树
}
}
二.后序创建二叉树
1.思路分析:
我们来先看看后序创建怎么创建的?顺序是 左子树->右子树->根节点, 如果按照先序递归的那种思想来实现后序创建是不可能的,因为父节点都没被创建哪来的创建其子节点?
我们观察一下后序的输入:##C###GE##FDB#A@
(提示:’@'是输入结束标志,后序输入需要加入输入结束标志)
再看看先序创建(根节点->右子树->左子树)输入是:A#BDF##EG###C##
我们可以发现后序和先序(根节点->右子树->左子树)的输入顺序刚好是相反的,所以我们可以用一个数组来存储后序输入的节点元素,然后通过先序创建的方式从数组末尾往前依次用节点元素来创建二叉树,也就是说后序创建本质上还是先序创建,只不过我们通过先序创建加上倒置赋值顺序实现了后序创建.
2.代码实现
//后序创建二叉树(非递归)
void PostCreateBiTree(BiTree& T)
{
TElemType data[500]; //创建一个存储元素的数组
int pos=0; //元素计数标记
TElemType d; //暂存输入的元素
scanf("%c",&d); //输入元素
while(d!='@') //输入'@'结束输入
{
data[pos++]=d; //将元素存入数组
scanf("%c",&d); //输入元素
}
if(pos==0) //如果无数据输入
{
T==NULL; //创建空树
return; //然后退出
}
BiTree stack[500]; //用数组创建一个栈
int top=0; //栈顶
int base=0; //栈底
//原理:先用数组存储元素,然后按根右左的顺序创建树,并从数组存储的元素从最后一个到第一个依次赋给节点
//即后序创建二叉树本质上还是先序创建,只是赋值顺序反了一下
BiTree *p=&T; //指向指向树节点的指针的指针
pos--; //退去表示输入结束字符
while(pos>=0) //存储元素的数组内还有元素没被存入树节点
{
if(data[pos]!='#') //不是创建空树的标志
{
(*p)=(BiTree)malloc(sizeof(BiTNode)); //给根节点分配空间
(*p)->lchild=(*p)->rchild=NULL; //左右子树暂时赋值为空
(*p)->data=data[pos--]; //根节点存入元素
stack[top++]=(*p); //根节点入栈
p=&((*p)->rchild); //向右走
}
else //创建空树的标志
{
pos--; //退去一个创建空树的标志字符
if(pos==0) //数组元素均被存入树节点
return; //退出函数
BiTree parent=stack[--top]; //获得父节点
p=&((parent)->lchild); //向左走
}
}
}
3.算法分析
这里我使用了先序非递归创建方法,当然也可以使用递归方法,不过要写两个函数,所以为了降低耦合性我就采用了先序非递归创建方法,也就是while(pos>=0){}这里面的算法,这里讲解一下这个算法
1.数组遍历位置不为0循环(非0说明有元素未被遍历并存入树节点中),如果遍历到的元素不是’#’,则指针p开辟内存空间创建节点并存储该元素,接着节点入栈(创建该节点的左子树需要通过其获得左子树的指针,所以暂时存入栈内),然后使p指针指向该节点的右子树(向右走)
2.如果遍历到的是’#’,那么就是创建空树喽,因为早已设置指针初始值就是指向空,所以不用再赋为空,直接遍历位置继续移动就行了,然后我们要出栈获取其父节点,通过父节点使p指向父节点的左子树(向左走)
3.就这样循环,最后数组内的元素被遍历完,二叉树就创建完成
三.层序创建二叉树
1.思路分析:
a.我们参照迷宫使用队列求解的思想,我们使用一个队列,利用其先进先出的性质.我们把节点的子节点按从左子树->右子树顺序依次创建并入队
b.一层节点创建完成后,进行出队,创建出队元素的子节点,也是同上原理,创建并入队
c.就这样队列非空循环,直到队列为空则二叉树创建完成
层序输入:AB#CD##EF#G####
2.代码实现
//层序创建二叉树(非递归)
void LevelCreateBiTree(BiTree& T)
{
BiTree queue[500]; //用数组创建一个队列
int front=0; //队头
int rear=0; //队尾
TElemType elem; //元素
scanf("%c",&elem); //输入根节点元素
if(elem=='#') //以'#'作为创建分叉结束
{
T=NULL;
return;
}
T=(BiTree)malloc(sizeof(BiTNode)); //给根节点分配空间
T->data=elem;
queue[rear++]=T; //根节点入队
BiTree p; //遍历树节点的指针
while(front!=rear)
{
p=queue[front++]; //出队
scanf("%c",&elem); //输入节点元素
if(elem!='#') //以'#'作为创建分叉结束
{
p->lchild=(BiTree)malloc(sizeof(BiTNode)); //创建左孩子
p->lchild->data=elem; //存入数据
queue[rear++]=p->lchild; //左孩子入队
}
else //否则子树为空
{
p->lchild=NULL;
}
scanf("%c",&elem); //输入节点元素
if(elem!='#') //以'#'作为创建分叉结束
{
p->rchild=(BiTree)malloc(sizeof(BiTNode)); //创建右孩子
p->rchild->data=elem; //存入数据
queue[rear++]=p->rchild; //右孩子入队
}
else //否则子树为空
{
p->rchild=NULL;
}
}
}
四.广义表形式创建二叉树
1.思路分析:
先来看看广义表的输入:A(B(C,D(E(,G),F)))#
(提示:’#'为输入结束标志,层序输入需要加入输入结束标志)
我们需要一个存储节点的栈,和标志创建左子树还是右子树的标记lflag,我们用一个循环遍历依次输入的元素
①如果遍历到的是数据元素,则新建一个树节点p,并把该元素存入新建节点p中,然后获取栈顶节点(新建节点的父节点),再判断标记lflag,如果lflag=1,表示要创建的是左子树,使其父节点左子树指针指向该新建节点p,如果lflag=0,表示要创建的是右子树,使其父节点右子树指针指向该新建节点p.
注意:当然我们还需要一个判断,当T== NULL,就不要进行出栈获取父节点的操作了,树根节点T不是某某节点的子节点(而且这时栈空),所以当T==NULL时,直接把p赋值给T
②如果遍历到的是左括号’(’ 表示准备构建节点的左子树,则令lflag=1(构建左子树的标记),同时p节点入栈(构建其子树需要通过该节点来获得子树的指针,所以暂时存入栈内)
③如果遍历到的是逗号’,’ 表示左子树构建完成,准备构建右子树,则令lflag=0(构建右子树的标记)
④如果遍历到的是右括号’)’ 表示节点的左右子树均构建完成,节点出栈(节点的左右子树都构建完成了,它已经任务完成了,没事做了,所以出栈)
就这样循环遍历,当遍历到结束字符’#'时结束循环,二叉树创建完成
2.代码实现
//用广义表创建二叉树
void GListCreateBiTree(BiTree &T)
{
BiTree stack[500]; //用数组创建栈
int top=0,base=0;
T=NULL; //初始树根为空
TElemType ch; //元素变量
scanf("%c",&ch); //输入元素
BiTree p; //节点指针
while(ch!='#')
{
int lflag; //标记变量,如果是1创建左子树,如果是0创建右子树
switch(ch)
{
case '(':stack[top++]=p; //遇到左括号父节点入栈,准备构建子树
lflag=1; //准备构建左子树的标记
break;
case ',': lflag=0; //遇到逗号,准备构建右子树的标记
break;
case ')':stack[--top]; //遇到右括号表示左右子树均构建完毕,父节点出栈
break;
default: //遇到数据元素
{
p=(BiTree)malloc(sizeof(BiTNode)); //创建新节点
p->lchild=p->rchild=NULL; //左右节点暂时赋值为空
p->data=ch; //存储元素
if(T==NULL) //初始构建树根
{
T=p;
}
else
{
BiTree parent=stack[top-1]; //获取父节点
if(lflag==1)
parent->lchild=p;
else if(lflag==0)
parent->rchild=p;
}
}//default
}//switch
scanf("%c",&ch); //已经处理完一个输入的字符,继续输入
}//while
}