二叉树的遍历操作

时间:2021-11-20 17:27:42

#include<stdio.h>
#include<stdlib.h>
#include<conio.h>

#define MAXSIZE 100
#define NULL 0
typedef char TElemtype;
typedef struct  BiTNode
{  
 TElemtype data;
 struct BiTNode *lchild,*rchild; //左右孩子指针
}BiTNode, *BiTree;

 

//先序创建二叉树链表
int CreateBiTree(BiTNode*&T) //之所以要用引用,是因为该函数中指针T会改变,而这种改变必须带回到主函数中
{ //按先序次序输入二叉树中结点的值(一个字符),#字符表示空树
 char ch;
 scanf("%c",&ch);
 getchar();
 if(ch=='#') T=NULL;
 else
 {
  T=(BiTNode *)malloc(sizeof(BiTNode));
  if(!T) return 0;
  else
  { 
   T->data=ch;
   CreateBiTree(T->lchild);
   CreateBiTree(T->rchild);
  }
 }
 return 1;
}

//按照层次非递归创建二叉树链表
int  CreateBiTree2(BiTNode*&T)
{
 //采用顺序队列存放
 BiTNode *s[MAXSIZE];
 int front=-1,rear=0; //队列的队首和队尾
 //创建根结点
 BiTNode *p;
 T=(BiTNode *)malloc(sizeof(BiTNode));
 if(!T) return 0;
 char ch=getchar();
 T->data=ch;
 //将根结点入队
 s[rear]=T; //这里没有采用循环队列,数据从下标0开始存储,当front=rear时,表明队列为空
 while(rear!=front)
 {
  //出队一个元素
  p=s[++front];
  p->lchild=NULL;
  p->rchild=NULL;
  //输入其2个儿子,其中注意表示空结点的特殊字符
  char ldata=getchar();
  char rdata=getchar();

  if(ldata!='#') //左孩子入队
  {
   if(!(p->lchild=(BiTNode *)malloc(sizeof(BiTNode)))) return 0;
   p->lchild->data=ldata;
   s[++rear]=p->lchild;
  }
  if(rdata!='#') //右孩子入队
  {
   if(!(p->rchild=(BiTNode *)malloc(sizeof(BiTNode)))) return 0;
   p->rchild->data=rdata;
   s[++rear]=p->rchild;
  }   
 }
}

//________________________________________________________
//按层次遍历二叉树链表,和按层次创建二叉树类似
void LeverTraverse(BiTNode *T)
{
 //采用顺序队列存放
 BiTNode *s[MAXSIZE];
 BiTNode *p;
 int front=-1,rear=0; //队列的队首和队尾
 //将根结点入队
 s[rear]=T; //这里没有采用循环队列,数据从下标0开始存储,当front=rear时,表明队列为空
 while(rear!=front)
 {
  //出队一个元素并输出
  p=s[++front];
  printf("%c",p->data);

  if(p->lchild) //左孩子入队
  {
   s[++rear]=p->lchild;
  }

  if(p->rchild) //右孩子入队
  {
   s[++rear]=p->rchild;
  }   
 }
}

//按层次遍历二叉树链表,和按层次创建二叉树类似
void LeverTraverse2(BiTNode *T)
{
 //采用顺序队列存放
 BiTNode *s[MAXSIZE];
 BiTNode *p;
 int front=-1,rear=0; //队列的队首和队尾
 //将根结点入队
 printf("%c",T->data);
 s[rear]=T; //这里没有采用循环队列,数据从下标0开始存储,当front=rear时,表明队列为空
 
 while(rear!=front)
 {
  //出队一个元素并输出
  p=s[++front];
  
  if(p->lchild) //左孩子入队
  {
   printf("%c",p->lchild->data);
   s[++rear]=p->lchild;
  }

  if(p->rchild) //右孩子入队
  {
   printf("%c",p->rchild->data);
   s[++rear]=p->rchild;
  }   
 }
}


//_______________________________________________________
//先序递归遍历二叉链表
void PreOrderTraverse(BiTNode *T)
{
 if(T)
 {
  printf("%c",T->data);
  PreOrderTraverse(T->lchild);
  PreOrderTraverse(T->rchild);
 }
}

//先序非递归遍历二叉树
void NPreOrderTraverse(BiTNode *T)
{
 //思路:模仿递归遍历的路径,采用栈来模拟,先根,再左右
 BiTNode *s[MAXSIZE];
 BiTNode *p=T;
 int top=-1;
 while(p||top!=-1)
 {
  while(p) //很显然这个while可以用if替代,循环采用外层的循环
  {//此时已输出从根到最左边的结点,并都已入栈
   printf("%c",p->data);
   s[++top]=p;
   p=p->lchild;
  }
  //将栈中左孩子依次出栈
  /*if(top!=-1)*/ p=s[top--]; 
  p=p->rchild;
 }
}

//还有更直接的思维,就是把访问根结点,然后右结点入栈,最后左结点入栈

//先序非递归遍历二叉树2
void NPreOrderTraverse2(BiTNode *T)
{
 //思路:模仿递归遍历的路径,采用栈来模拟,先根,再左右
 BiTNode *s[MAXSIZE];
 BiTNode *p=T;
 int top=-1;
 while(p||top!=-1)
 {
  if(p) //很显然这个while可以用if替代,循环采用外层的循环
  {    //此时已输出从根到最左边的结点,并都已入栈
   printf("%c",p->data);
   s[++top]=p;
   p=p->lchild;
  }
  else
  {
   //将栈中左孩子依次出栈
   /*if(top!=-1)*/ p=s[top--]; 
   p=p->rchild;
  }
 }
}

//________________________________________________________________________
//中序递归遍历二叉链表
void InOrderTraverse(BiTNode *T)
{
 if(T)
 {
  InOrderTraverse(T->lchild);
  printf("%c",T->data);  
  InOrderTraverse(T->rchild);
 }
}

//中序非递归遍历二叉树
void NInOrderTraverse(BiTNode *T)
{
 //思路:模仿递归遍历的路径,采用栈来模拟,先左,再根,最后右
 BiTNode *s[MAXSIZE];
 BiTNode *p=T;
 int top=-1;
 while(p||top!=-1)
 {
  while(p) //很显然这个while可以用if替代,循环采用外层的循环
  {//此时将根到最左边入栈,但不输出
   s[++top]=p;
   p=p->lchild;
  }
  //将栈中左孩子依次出栈,并输出
  /*if(top!=-1)*/ p=s[top--];
  printf("%c",p->data);
  p=p->rchild;
 }
}

//中序非递归遍历二叉树2
void NInOrderTraverse2(BiTNode *T)
{
 //思路:模仿递归遍历的路径,采用栈来模拟,先左,再根,最后右
 BiTNode *s[MAXSIZE];
 BiTNode *p=T;
 int top=-1;
 while(p||top!=-1)
 {
  if(p) //很显然这个while可以用if替代,循环采用外层的循环
  {//此时将根到最左边入栈,但不输出
   s[++top]=p;
   p=p->lchild;
  }
  else
  {
   //将栈中左孩子依次出栈,并输出
   /*if(top!=-1)*/ p=s[top--];
   printf("%c",p->data);
   p=p->rchild;
  }
 }
}

//__________________________________________________________________________
//后序递归遍历二叉链表
void PostOrderTraverse(BiTNode *T)
{
 if(T)
 {
  PostOrderTraverse(T->lchild);  
  PostOrderTraverse(T->rchild);
  printf("%c",T->data);
 }
}

//后序非递归遍历二叉树
void NPostOrderTraverse(BiTNode *T)
{
 //思路:模仿递归遍历的路径,采用栈来模拟,先左右,再根
 //这里没有采用辅助数组,只是应用了当某结点的前一个被访问结点是其右孩子,或其右孩子是NULL,则输出
 BiTNode *s[MAXSIZE];
 BiTNode *p=T,*pre=NULL; //pre指示当前结点的前一结点
 int top=-1;
 while(p||top!=-1)
 {
  while(p)
  {
   s[++top]=p;
   p=p->lchild;
  } //一直到二叉树最左边结点
  p=s[top--];
  if(p->rchild==NULL||p->rchild==pre) //当p->rchild刚好是上一个已访问的结点时,访问此结点
  {
   printf("%c",p->data);
   pre=p;
   p=NULL;  //让它跳过while(p),否则进入死循环
  }
  else
  {
   s[++top]=p;//p必须重新入栈,或者前面的p=s[top--]改为p=s[top]也可;
   //所以,中序或后序的非递归算法可以采用比对入栈的次数来实现
   p=p->rchild;//让右结点进栈
  }
 }
}

//后序非递归遍历二叉树2
void NPostOrderTraverse2(BiTNode *T)
{
 //思路:模仿递归遍历的路径,采用栈来模拟,先左右,再根
 //这里采用辅助数组
 BiTNode *s[MAXSIZE];
 int tag[MAXSIZE]; //设计一个辅助数组,指示其右孩子是否被访问了
 BiTNode *p=T;
 int top=-1;

 while(p||top!=-1)
 {
  while(p)
  {
   ++top;
   s[top]=p;
   tag[top]=0; //设置右孩子没访问
   p=p->lchild;
  }
  p=s[top];
  if(p->rchild==NULL||tag[top]==1)  //这里可以用内循环,只要满足这个条件就出栈输出
  {
   printf("%c",p->data);
   top--;
   p=NULL; //让它跳过while(p),否则进入死循环
  }
  else
  {
   tag[top]=1;
   p=s[top]->rchild;
  }

 }

 /* 参考代码
 void NPostOrderTraverse3(BiTree *T)
 {
 BiTree p;
 Stack *st;
 initstack(st);
 p=T;
 int Tag[20];      //栈,用于标识从左(0)或右(1)返回
 while (p!=NULL || !isempty(st))
 {
 while (p!=NULL)
 {
 push(st,p);
 Tag[st->top]=0;
 p=p->lchild;
 }
 while (!isempty(st)&&Tag[st->top]==1)
 {
 p=pop(st);
 cout<<p->data<<"  ";
 //这里是不是少了p=NULL?,不需要,因为运行此处while后转入下面的if,那么由p=gettop(st)来改变p
 }
 if (!isempty(st))
 {
 Tag[st->top]=1;   //设置标记右子树已经访问,无论右子节点是否为空或一个实体
 p=gettop(st);
 p=p->rchild;  
 }
 else break;
 }*/
 //_____________________________________________________________________ 
}
//谭浩强数据结构辅导书里面的后序非递归遍历挺有意思的

//附注:愈勇编写的二叉树中序、后序非递归遍历,采用判断结点到底是第几次入栈,
//即:先让根结点入栈,出栈,若左孩子非空,则让根结点入栈,再让左孩子入栈等等;若非叶子结点入栈次数等于2或3就输出
typedef struct  BiTNode2
{  
 TElemtype data;
 struct BiTNode2 *lchild,*rchild; //左右孩子指针
 int num;
}BiTNode2, *BiTree2;


//先序创建二叉树链表
int CreateBiTree3(BiTNode2*&T) //之所以要用引用,是因为该函数中指针T会改变,而这种改变必须带回到主函数中
{ //按先序次序输入二叉树中结点的值(一个字符),#字符表示空树
 char ch;
 scanf("%c",&ch);
 getchar();
 if(ch=='#') T=NULL;
 else
 {
  T=(BiTNode2 *)malloc(sizeof(BiTNode2));
  if(!T) return 0;
  else
  { 
   T->data=ch;
   CreateBiTree3(T->lchild);
   CreateBiTree3(T->rchild);
  }
 }
 return 1;
}
void NPostOrderTraverse4(BiTNode2 *T)
{
   
 BiTNode2 *s[MAXSIZE];
 BiTNode2 *p=T;
 int top=-1;

 while(p||top!=-1)
 {
  while(p)
  {
   ++top;
   p->num=0;
   s[top]=p;
   p=p->lchild;
  }
  p=s[top--]; //表示第一次出栈出栈
  p->num++;   //表示此时出栈次数为1
  if(p->num==2||p->rchild==NULL)
  {
   printf("%c",p->data);
   p=NULL;
  }
  else if(p->rchild)
  {
   s[++top]=p; //p再次入栈
   p=p->rchild;
  }
 }
}

void main()
{
 BiTNode2*T=NULL;
 printf("请先序创建二叉树链表\n");
 CreateBiTree3(T);

 //printf("\n请先序递归遍历二叉链表\n");
 //PreOrderTraverse(T);

 //printf("\n请先序非递归遍历二叉链表\n");
 //NPreOrderTraverse2(T);

 //printf("请按层次创建二叉树链表\n");
 //CreateBiTree2(T);

 //printf("\n请按层次遍历二叉链表\n");
 //LeverTraverse2(T);

 //printf("\n请中序递归遍历二叉链表\n");
 //InOrderTraverse(T);

 //printf("\n请中序非递归遍历二叉链表\n");
 //NInOrderTraverse2(T);

 //printf("\n请后序递归遍历二叉链表\n");
 //PostOrderTraverse(T);

 printf("\n请后序非递归遍历二叉链表\n");
 NPostOrderTraverse4(T);

 getchar();
}