二叉树的非递归遍历:先序、中序、后续、层序。
其中前三种用了栈来辅助存储结点,层序利用了队列来辅助存储结点。
后序较前面两种遍历稍微复杂点,因为根结点是最后输出的,所有根结点要访问到2次,在第二次访问时输出,需要做辅助标记。
为了简化问题,二叉树也简单的形同:ABC##DE###FG### #代表空结点。
当然,为了和前面的知识点联系起来,stack和queue都是自己写的,没有用STL容器里的,当然换容器写的话就要简洁很多。
代码:
#include<stdio.h>
#include<string.h>
#include<malloc.h>
#define InitStackSize 100
#define MaxQueueSize 100
#define Increase 100
typedef char Elemtype;
typedef struct TNOde
{
struct TNOde *lchild,*rchild;
Elemtype data;
}TNOde,*BinTree;
typedef struct SqStack
{
BinTree *base,*top;
int stacksize;
int length;
}SqStack;
typedef struct SqQue
{
BinTree *base;
int front;
int rear;
}SqQue;
//栈的操作
void InitStack(SqStack &S)
{
S.base=S.top=(BinTree*)malloc(InitStackSize*(sizeof(BinTree)));
S.stacksize=InitStackSize;
S.length=0;
}
void Push(SqStack &S,BinTree q)
{
if(S.top-S.base>=S.stacksize)
{
S.base=(BinTree*)realloc(S.base,(Increase+S.stacksize)*sizeof(BinTree));
S.top=S.base+S.stacksize;
S.stacksize+=Increase;
}
*S.top=q;
S.top++;
S.length++;
}
bool EmptyStack(SqStack S)
{
if(S.base==S.top) return true;
else return false;
}
BinTree StackTop(SqStack S)
{
BinTree e;
e=*--S.top;
return e;
}
void PopStack(SqStack &S)
{
S.top--;
S.length--;
}
///以上栈的操作
///队列的操作
void InitQue(SqQue &Q)
{
Q.base=(BinTree*)malloc(MaxQueueSize*(sizeof(BinTree)));
Q.front=Q.rear=0;
}
void PushQue(BinTree T,SqQue &Q)
{
Q.base[Q.rear]=T;
Q.rear=(Q.rear+1)%MaxQueueSize;
}
bool EmptyQue(SqQue Q)
{
if(Q.front==Q.rear) return true;
else return false;
}
BinTree PopQue(SqQue &Q)
{
BinTree e;
e=Q.base[Q.front];
Q.front=(Q.front+1)%MaxQueueSize;
return e;
}
///以上队列的操作
以下二叉树操作
void CreatTree(BinTree &T) //先序递归构造二叉树 形如ABC##DE###FG###
{
Elemtype ch;
scanf("%c",&ch);
if(ch=='#') T=NULL;
else
{
T=(TNOde*)malloc(sizeof(TNOde));
T->data=ch;
CreatTree(T->lchild);
CreatTree(T->rchild);
}
}
void PreOrTree(BinTree T) //非递归先序遍历
{
SqStack s;
BinTree p;
p=T;
InitStack(s);
while(p || !EmptyStack(s))
{
if(p)
{
printf("%c",p->data); //访问根结点
Push(s,p); //根结点入栈
p=p->lchild; //访问左孩子
}
else
{
p=StackTop(s); //左孩子访问结束 根结点出栈
PopStack(s);
p=p->rchild; //访问右结点
}
}
printf("\n");
}
void InOrTree(BinTree T) //中序非递归遍历
{
SqStack s;
BinTree p;
p=T;
InitStack(s);
while(p || !EmptyStack(s))
{
if(p)
{
Push(s,p); //根结点存在,入栈
p=p->lchild; //访问左孩子
}
else
{
p=StackTop(s); //左孩子访问过 输出根结点
PopStack(s); //根结点出栈
printf("%c",p->data);
p=p->rchild; //访问右结点
}
}
printf("\n");
}
void PostOrTree(BinTree T) //后续非递归遍历
{
BinTree p,q;
SqStack s;
int mark[InitStackSize]; //为了问题简便,假设初始栈容量够用,不然就要溢出了
memset(mark,0,sizeof(mark));
p=T;
InitStack(s);
while(p || !EmptyStack(s))
{
if(p) //从树(子树)根结点开始 一直访问左子树 找到左下角叶结点 途中所经过的结点
{
Push(s,p);
mark[s.length]=0; //一直找左子树,右子树未访问 mark置0
p=p->lchild;
}
else //左子树遍历完后
{
if(mark[s.length]==0) //栈顶右子树未访问,访问右子树
{
mark[s.length]=1; //第一次访问右子树 置1
p=StackTop(s); //取栈顶元素 访问其右子树
p=p->rchild;
}
else //mark已经是1 说明已经访问过右子树了
{
q=StackTop(s);
printf("%c",q->data); //输出根结点
PopStack(s); //栈中结点删除
}
}
}
printf("\n");
}
/*
层序遍历思路:
1.根结点非空,入队;
2.队列非空,队头出队,输出队头,如果左孩子存在,左孩子入队,如果右孩子存在,右孩子入队;
3.重复上述步骤,访问所有结点。
*/
void LevelOrdTree(BinTree T) //非递归层序遍历
{
BinTree p;
SqQue que;
InitQue(que);
if(T)
{
PushQue(T,que);
}
while(!EmptyQue(que))
{
p=PopQue(que); //为了方便,写元素直接出队,并返回该元素 省去先GetTop 然后元素Pop
printf("%c",p->data);
if(p->lchild) PushQue(p->lchild,que); //左孩子不空 入队
if(p->rchild) PushQue(p->rchild,que); //右孩子不空 入队
}
printf("\n");
}
int DepthTree(BinTree T) //二叉树深度
{
int ldepth,rdepth;
if(T==NULL) return 0;
ldepth=DepthTree(T->lchild);
rdepth=DepthTree(T->rchild);
return (ldepth>rdepth?ldepth:rdepth)+1;
}
int LeavesNum(BinTree T) //二叉树叶子结点数
{
if(T==NULL) return 0;
if(T->lchild==NULL && T->rchild==NULL) return 1;
return LeavesNum(T->lchild)+LeavesNum(T->rchild);
}
int main()
{
BinTree Tree;
CreatTree(Tree);
printf("PreOrder travel Tree:\n"); //非递归先序遍历
PreOrTree(Tree);
printf("InOrder travel Tree:\n");//非递归中序遍历
InOrTree(Tree);
printf("PostOrder travel Tree:\n");//非递归后序遍历
PostOrTree(Tree);
printf("Level order treval Tree:\n"); //非递归层序遍历
LevelOrdTree(Tree);
printf("The depth of the Tree:%d\n",DepthTree(Tree));//二叉树深度
printf("The leaves number:%d\n",LeavesNum(Tree)); //二叉树叶子结点数
return 0;
}