树的三种递归、非递归遍历

递归和非递归俩种方法实现二叉树的前序遍历。

咱们先来复习下,基础知识。
因为关于树的遍历,在此100题中已出现过太多次了。

二叉树结点存储的数据结构:
typedef char datatype;
typedef struct node
 {
   datatype data;
   struct node* lchild,*rchild;
 } bintnode;

typedef bintnode* bintree;
bintree root;

 

1.树的前序遍历即:
按根 左 右 的顺序,依次
前序遍历根结点->前序遍历左子树->前序遍历右子树

前序遍历,递归算法
void preorder(bintree t)    
 //注,bintree为一指向二叉树根结点的指针
{
   if(t)
    {
      printf("%c",t->data);
      preorder(t->lchild);
      preorder(t->rchild);
    }
}


然后,依葫芦画瓢,得到....


2.中序遍历,递归算法
void preorder(bintree t)
{
   if(t)
    {

      inorder(t->lchild);
      printf("%c",t->data);
      inorder(t->rchild);
    }
}

 

3.后序遍历,递归算法
void preorder(bintree t)
{
   if(t)
    {

      postorder(t->lchild);
      postorder(t->rchild);
      printf("%c",t->data);
    }
}

 

二叉树的创建方法,
void createbintree(bintree* t)
{
  char ch;
  if( (ch=getchar())==' ')
    *t=NULL;
  else
   {
     *t=(bintnode*) malloc(sizeof(bintnode));
     (*t)->data=ch;
     createbintree(&(*t)->lchild);
     createbintree(&(*t)->rchild);
   }
}


接下来,
咱们在讨论二叉树遍历算法的非递归实现之前,
先看一个顺序栈的定义及其部分操作的实现
typedef struct stack
{
  bintree data[100];
  int tag[100];
  int top;
}seqstack;

void push(seqstack* s,bintree t)
{
  s->data[s->top]=t;
  s->top++;
}

bintree pop(seqstack* s)   //出栈
{
  if(s->top!=0)
   {
     s->top--;
     return (s->data[s->top]);
   }
  else
     return NULL;
}


好了,现在,我们可以看二叉树前序遍历的非递归实现了。
按照二叉树前序遍历的定义,无论是访问整棵树还是其子树,均应该遵循先访问根结点,
然后访问根结点的左子树,最后访问根结点的右子树的。

 

因为对于一棵树(子树)t,如果t非空,访问完t的根结点值后,就应该进入t的左子树,
但此时必须将t保存起来,以便访问完其左子树后,进入其右子树的访问。
yeah,就是这个意思。:)...

即在t处设置一个回溯点,并将该回溯点进栈保存。


在整个二叉树前序遍历的过程中,程序始终要做的工作分成俩个部分:
1.当前正在处理的树(子树)
2.保存在栈中等待处理的部分。

//注:当栈中元素位于栈顶即将出栈时,意味着其根结点和左子树已访问完成,
//出栈后,进入其右子树进行访问,
//前序遍历,非递归实现
void preorderT(bintree t)
{
  seqstack s;
  s.top=0;
  while( (t)||(s.top!=0) )  //当前处理的子树不为空或栈不为空
  {
    while(t)         //子树不为空
    {
      printf("%c",t->data);   //1.先访问根结点
      push(&s,t);             //2.访问左子树之前,记得先把根结点进栈保存 
      t=t->lchild;            //3.然后才访问左子树,
    }
    if(s.top>0)      //栈不为空
    {
      t.pop(&s);               //4.pop根结点
      t=t->rchild;             //5.访问右子树
    }
  }
}


//中序遍历,非递归实现,
void inorderT(bintree t)
{
  seqstack s;
  s.top=0;
  while( (t)||(s.top!=0) )  //当前处理的子树不为空或栈不为空
  {
    while(t)     //子树不为空
    {
      push(&s,t);             //1.访问左子树之前,记得先把根结点push进栈
      t=t->lchild;            //2.访问左子树
    }
    if(s.top!=0)    //栈不为空
    {
      t.pop(&s);             //3.pop根结点(访问完左子树后)
      printf("%c",t->data);  //4.访问根结点 (把先前保存的t给拿出来,要用了..)
      t=t->rchild;           //5.访问右子树
    }
  }
}

 

//后序遍历,非递归实现
后序遍历的非递归算法,稍微复杂点。请看,

按照二叉树后序遍历的定义,无论是访问整棵树还是起子树,
均应该遵循先访问根结点左子树,然后访问根结点的右子树,最后访问根结点。

值得注意的是,当一个元素位于栈顶即将处理的是,其左子树的访问一定完成,
如果其右子树不为空,接下来应该进入其右子树尽情访问。
//注意了,
但此时该栈顶元素时不能出栈的,因为它作为根结点,其本身的值还未被访问。
只有等到其右子树也访问完成后,该栈顶元素才能出栈,并输出它的值。

因此,在二叉树后序遍历的算法中,必须使用seqstack类型的数组tag,
其每个元素取值为0或1,用于标识栈中每个元素的状态。


1.当一个元素刚进栈时,其对应的tag值置为0;
2.当它位于栈顶即将被处理时,其tag值为0.意味着应该访问其右子树。
于是将右子树作为当前处理的对象,此时该栈顶元素仍应该保留在栈中。
并将其对应的tag值改为1.
3.当其右子树访问完成后,该元素又一次位于栈顶,而此时其tag值为1,
意味着其右子树已访问完成,接下来,应该直接访问的就是它,将其出栈。

 

void postorderT(bintree t)
{
  seqstack s;
  s.top=0;
  while( (t)||(s.top!=0) )
  {
    while(t)
    {
      s.data[s.top]=t;
      s.tag[s.top]=0;   //tag置为0
      s.top++;
      t=t->lchild;      //访问左子树
    }
    while( (s.top>0)&&(s.tag[s.top-1]==1) )        
    {
      s.top--;
      t=s.data[s.top];
      printf("%c",t->data);
    }
    if(s.top>0)
    {
      t=s.data[s.top-1];
      s.tag[s.top-1]=1;
      t=t->rchild;
    }
    else
      t=NULL;
  }
}
至此,完。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值