目录
一、遍历二叉树算法描述
遍历二叉树是指按照某条搜索路径巡访树中的每个结点,使得每个结点都被访问到并且只访问一次,若限定先左后右的访问方式,则有三种情况:
(1)先序:根左右(DLR)
(2)中序:左根右(LDR)
(3)后序:左右根(LRD)
先序遍历二叉树的操作定义如下:(1) 先访问根结 (2)先序遍历左子树 (3)先序遍历右子树
中序遍历二叉树的操作定义如下:(1)先序遍历左子树 (2)访问根结点 (3)先序遍历右子树
后序遍历二叉树的操作定义如下:(1)先序遍历左子树 (2)先序遍历右子树 (3)访问根结点
下图可帮助加深一下理解:
1、递归遍历二叉树
以下这三种递归遍历除输出语句的位置不同外,其他算法描述均相同,递归遍历实际上是自动调用了系统的工作栈
先序遍历的递归算法:
void XianTraverse(BiTree T)
{
if(T!=NULL){
printf("%c",T->data);
XianTraverse(T->lchild);
XianTraverse(T->rchild);
}
}
中序遍历的递归算法:
void InOrderTraverse(BiTree T)
{
if(T!=NULL){
InOrderTraverse(T->lchild);
printf("%c",T->data);
InOrderTraverse(T->rchild);
}
}
后序遍历的递归算法:
void HouTraverse(BiTree T)
{
if(T!=NULL){
HouTraverse(T->lchild);
HouTraverse(T->rchild);
printf("%c",T->data);
}
}
2、非递归遍历二叉树
非递归遍历同样用到栈,该栈与递归遍历所用栈不相同,非递归用到的栈需要用户向系统申请,模拟系统工作栈的原理,以先序遍历二叉树为例,需要用一个指针变量p来存储当前访问的结点,用q来存储当前出栈元素的地址,当p的指向不为空或者是栈不为空时循环执行以下操作:
- 如果p不为空 1.输出p的data域 2.将p所指元素入栈 3. p指向p的左孩子
- 如果p为空 1.将栈顶元素弹出栈,并将弹出元素赋值给q 2. p指向p的右孩子
用下图来表示 一棵树按照先序的方式进行遍历的全部过程
需注意输出的过程即为元素入栈的过程
代码如下:
void InOrderTraversenot(BiTree T)
{
InitStack(S);
p=T;
q=new BiTNode;
while(p!=NULL || StackEmpty(S)!=0){
if(p!=NULL){
printf("%c",p->data);
Push(S,p);
p=p->lchild;
}
else{
Pop(S,q);
p=q->rchild;
}
}
}
二、根据遍历序列确定二叉树
根据二叉树的先序序列和中序序列或者是后序序列和中序序列可以唯一的确定一颗二叉树
例如,给定一颗二叉树的后序序列为:DECBGFA 中序序列为:BDCEAFHG,请画出该树,以下为该题的题解
- 后序的遍历方式为左右根,则后序序列的最后一个元素为该二叉树的根,可以确定该二叉树的根为A
- 由上一步可知该树的根为A,那么由中序序列可知BDCE为A的左子树,FHG为A的右孩子
- 先来看A的左子树 ,由后序序列(左右根)可知在BDCE中B为根(DECBGFA),而DCE都是B的孩子
- 由中序序列(根左右)可知B没有左孩子,只有右孩子(BDCEAFHG)
- 由后序序列(左右根)可知在DCE中,C为根(DECBGFA ),D和E都是C的孩子
- 由中序序列(根左右)可知C的左孩子是D,右孩子是E(BDCEAFHG)
- 同样方法可以确定A的右子树是如何排序的
最后确定该二叉树如图所示
三、二叉树遍历算法的应用
1、创建二叉树的存储结构 —— 二叉链表
如果在遍历过程中生成结点,这样就可以建立二叉树的存储结构,假设按先序遍历的顺序建立二叉链表,T为指向根节点的指针,对于一个给定的字符序列,依次读入字符,从根结点开始,递归创建二叉树
操作步骤:
1、读取字符序列,读入字符ch
2、如果ch=='#',则表明该二叉树为空,即T=NULL,否则执行以下操作
- 申请一个结点空间T
- 将ch赋值给T->data
- 递归创建T的左子树
- 递归创建T的右子树
算法描述:
void CreateBiTree(BiTree &T)
{
scanf("%c",&ch);
if(ch=='#') T=NULL;
else{
T=new BiTNode;
T->data=ch;
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
}
}
如下图所示的二叉树按照先序序列创建时,输入的顺序为:ABC##DE#G##F###
2、复制二叉树
复制二叉树的实现与二叉树先序遍历的实现非常相似
若二叉树不为空,则首先复制根结点,这 相当于二叉树先序遍历算法中的访问根结点语句,然后分别复制二叉树根节点的左子树和右子树,这相当于遍历中递归遍历左子树和右子树的语句
操作步骤:
如果树是空树,递归结束,否则执行以下操作
- 申请一个新的结点空间,复制根结点
- 递归复制左子树
- 递归复制右子树
算法描述:
void Copy(BiTree T,BiTree &NewT)
{
if(T==NULL){
NewT=NULL;
return;
}
else{
NewT=new BiTNode;
NewT->data=T->data;
Copy(T->lchild,NewT->lchild);
Copy(T->rchild,NewT->rchild);
}
}
3、计算二叉树的深度
二叉树的深度为树中结点的最大层次,二叉树的深度为左右子树深度的较大者加1(根节点)
操作步骤:
如果是空树,递归结束,深度为0,否则执行以下操作
- 递归计算左子树的深度记为m
- 递归计算右子树的深度记为n
- 如果m大于n,二叉树的深度为m+1,否则为n+1
算法描述:
int Depth(BiTree T)
{
if(T==NULL) return 0;
else{
int m=Depth(T->lchild);
int n=Depth(T->lchild);
if(m>n) return (m+1);
else return (n+1);
}
}
4、统计二叉树中结点的个数
操作步骤:
如果是空树,则结点的个数为0,递归结束;否则,节点的个数为左子树的结点个数加上右子树的结点个数再加1(根结点)
算法描述:
Status NodeCount(BiTree T)
{
if(T==NULL) return 0;
else
return NodeCount(T->lchild)+NodeCount(T->rchild)+1;
}
补充:统计叶结点的总个数
Status LeafNodeCount(BiTree T)
{
if(T==NULL) return 0;
else if(T->lchild==NULL && T->rchild==NULL)
return 1;
else
return LeafNodeCount(T->lchild)+LeafNodeCount(T->rchild);
}
四、总体代码
#include<stdio.h>
#define MAXSIZE 100
#define OK 1
#define ERROR 0
typedef int Status;
//二叉链表的存储表示
typedef struct BiTNode{ /*节点结构声明*/
char data ; /*节点数据*/
struct BiTNode *lchild;
struct BiTNode *rchild ; /*指针*/
}*BiTree, BiTNode;
typedef BiTree SElemType;
//顺序栈的存储结构
typedef struct
{
SElemType *base;
SElemType *top;
int stacksize;
}SqStack;
//栈的初始化
Status InitStack(SqStack &S)
{
S.base=new SElemType[MAXSIZE];
S.top=S.base;
S.stacksize=MAXSIZE;
return OK;
}
//入栈
Status Push(SqStack &S,SElemType e)
{
if(S.top-S.base==S.stacksize) return ERROR;
*S.top=e;
S.top++;
return OK;
}
//出栈
Status Pop(SqStack &S,SElemType &e)
{
if(S.top==S.base) return ERROR;
S.top--;
e=*S.top;
return OK;
}
//判断栈是否为空
Status StackEmpty(SqStack S)
{
if(S.base==S.top) return ERROR;
return OK;
}
//先序创建一棵树
void CreateBiTree(BiTree &T)
{
char ch;
scanf("%c",&ch);
if(ch=='#') T=NULL;
else{
T=new BiTNode;
T->data=ch;
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
}
}
//先序递归输出
void XianTraverse(BiTree T)
{
if(T!=NULL){
printf("%c",T->data);
XianTraverse(T->lchild);
XianTraverse(T->rchild);
}
}
//中序递归输出
void InOrderTraverse(BiTree T)
{
if(T!=NULL){
InOrderTraverse(T->lchild);
printf("%c",T->data);
InOrderTraverse(T->rchild);
}
}
//后序递归输出
void HouTraverse(BiTree T)
{
if(T!=NULL){
HouTraverse(T->lchild);
HouTraverse(T->rchild);
printf("%c",T->data);
}
}
//非递归遍历
void InOrderTraversenot(BiTree T)
{
SqStack S;
InitStack(S);
BiTree p,q;
p=T;
q=new BiTNode;
while(p!=NULL || StackEmpty(S)!=0){
if(p!=NULL){
Push(S,p);
p=p->lchild;
}
else{
Pop(S,q);
printf("%c",q->data);
p=q->rchild;
}
}
}
//计算二叉树的深度
int Depth(BiTree T)
{
if(T==NULL) return 0;
else{
int m=Depth(T->lchild);
int n=Depth(T->lchild);
if(m>n) return (m+1);
else return (n+1);
}
}
//求结点总数
Status NodeCount(BiTree T)
{
if(T==NULL) return 0;
else
return NodeCount(T->lchild)+NodeCount(T->rchild)+1;
}
//求叶子结点数
Status LeafNodeCount(BiTree T)
{
if(T==NULL) return 0;
else if(T->lchild==NULL && T->rchild==NULL)
return 1;
else
return LeafNodeCount(T->lchild)+LeafNodeCount(T->rchild);
}
int main()
{
BiTree T;
CreateBiTree(T);//用先序递归创建下面的一棵二叉树T1
printf("先序输出:");
XianTraverse(T);//先序递归输出二叉树中的每一个节点;
printf("\n中序输出:");
InOrderTraverse(T);//中序递归输出二叉树中的每一个节点
printf("\n后序输出:");
HouTraverse(T);//后序递归输出二叉树中的每一个节点
printf("\n中序非递归输出:");
InOrderTraversenot(T);//用非递归的方法遍历输出树中的每一个节点(中序)
printf("\n");
int de=Depth(T);
printf("该二叉树的深度为:%d\n",de);
int nc=NodeCount(T);
int lc=LeafNodeCount(T);
printf("该二叉树的结点总数为:%d\n该二叉树的结叶子点总数为%d\n",nc,lc);
}
运行截图:
该二叉树如下图所示,可自行验证: