二叉树的递归遍历与非递归算法实现

原创 2013年12月05日 11:51:29
       通过递归算法与非递归算法的比较,更好地理解各自的特点。非递归其实就是调用栈的基本操作,进栈,出栈等。
这里面也正好复习了下栈的基本算法的实现。
 
栈和队列的实现在我的前一篇博文里。
 
基本数据结构
typedef struct BiNode
{
      char data;   //此处,二叉树中节点值类型为字符型
      struct BiNode *lchild,*rchild;     //左右孩子节点
}BiNode,*BiTree;   
 
二叉树的创建
先申请根节点的空间,然后赋值,然后分别递归建立其左子树和右子树
//按照先序序列输入构建一棵二叉树
void Create(BiTree &T)
{
	char ch;
	scanf("%c",&ch);
	if( '#' == ch )
	{
		T = NULL;
	}
	else
	{
		T=(BiNode *)malloc(sizeof(BiNode));
		T->data=ch;
		Create(T->lchild);
		Create(T->rchild);
	}
}

二叉树的递归遍历算法
//二叉树的遍历,三种顺序的递归遍历,其实就是访问根节点的顺序不同,这里的访问操作是打印结点,也可以是对结点其他的操作
//二叉树的先序遍历
void PreOrder(BiTree T)
{
      if(T)
     {
           printf( "%c ",T->data);
           PreOrder(T->lchild);
           PreOrder(T->rchild);
     }
}
//二叉树的中序遍历
void InOrder(BiTree T)
{
      if(T)
     {
           InOrder(T->lchild);
           printf( "%c ",T->data);
           InOrder(T->rchild);
     }
}
 
//二叉树的后序遍历
void PostOrder(BiTree T)
{
      if(T)
     {
           PostOrder(T->lchild);
           PostOrder(T->rchild);
           printf( "%c ",T->data);
     }
}

二叉树的非递归遍历算法
// 二叉树先序遍历的非递归算法
/* 设置一个存放结点指针的栈 S ,从根结点开始,每访问一结点后,按先序规则走左子树,若右子树存在,
则将右子树指针进栈,以便以后能正确地返回到该右子树上进行遍历访问。
*/
void PreTraverse(BiTree T)
{
     BiNode *p;
     Stack S;
     S=InitStack();
      if (T)
     {
           Push(S,T);  // 根结点指针进栈
            while (!StackEmpty(S))
           {
                p=Pop(S);  // 出栈,栈顶元素赋给 p
                 while (p)
                {
                     printf( "%c\t" ,p->data);  // 访问 p结点
                      if (p->rchild)
                           Push(S,p->rchild);  // 右子树存在时,进栈
                     p=p->lchild;            // 继续沿着左子树往下走
                }
           }
     }
}
/*
说明:内部循环是从 p 结点出发一直走到最左,走的过程中保存了每一个右子树的地址,
(因为右子树还没有被访问)而且是先进后出的,即先保存的比后保存的更先被用作返回地址,
所以是用栈。外循环正好是当内部循环不下去的时候,退一栈的情形。即换成他的右子树。
*/
 
// 二叉树的中序遍历的非递归算法
/*
同前序遍历 , 栈S 存放结点指针。对每棵子树 ( 开始是整棵二叉树 ),沿左找到该子树在中序下的第一结点
( 但寻找路径上的每个结点指针要进栈 ), 访问之; 然后遍历该结点的右子树 ,
又寻找该子树在中序下的第一结点, .. …直到栈S 空为止。
*/
void InTraverse(BiTree T)
{
     BiNode *p;
     Stack S;
     S=InitStack();
     Push(S,T);  // 根结点指针进栈
      while (!StackEmpty(S))
     {
            while ((p=GetsTop(S))&&p)  //取栈顶元素且存在,赋给 p
                Push(S,p->lchild);    //p 的左子树进栈
           p=Pop(S);               // 去掉最后的空指针
            if (!StackEmpty(S))
           {
                p=Pop(S);       // 弹出栈顶元素,赋给 p
                printf( "%c\t" ,p->data);   // 访问 p结点
                Push(S,p->rchild);     // 右子树进栈,然后遍历右子树
           }
     }
}
 
/*
说明:和前序不一样,这里的栈保存的是根结点的地址(因为中序遍历先访问左子树,
而根结点没有被访问到。而前序遍历不一样,他一开始就访问根结点,
所以他不保存根结点的地址而是保存右子树的地址,因为右子树还没有被访问。
总之,用栈就是为了帮我们保存还没有被访问的地址,以便将来我们能找到返回的地址)。
*/
 
/* 后序遍历二叉树的非递归算法 */
/* 对一个结点是否能访问,要看他的左、右子树是否遍历完, */
/* 所以每一个结点对应一个标志位 -tag 。tag=0 ,表示该结点暂不能访问; tag=1 ,表示该结点可以访问 */
/* 其实是区分这次返回是遍历完左子树返回的还是遍历完右子树返回的,如果是左子树返回的那么就不能访问根结点, */
/* 如果是右子树返回的就能访问根结点。当搜索到某 P 结点时,先要遍历其左子树,因而将结点地址 P 及tag=0 进栈; */
/* 当P 结点的左子树遍历完之后,再遍历其右子树,又将地址 P 及tag=1 进栈;当 P结点右子树遍历完之后( tag=1 ),便可对 P结点进行访问 */
void PostTraverse(BiTree T)
{
      int tag;
     BiNode *p;
     Stacks S;
     SNode sdata;
     S=InitStacks();
     p=T;
      while (p||!StacksEmpty(S))
     {
            while (p)
           {
                sdata.q=p;
                sdata.tag=0;
                Pushs(S,&sdata);   //(p,0) 进栈
                p=p->lchild;      // 遍历p 之左子树
           }
           sdata=*Pops(S);  // 退栈
           p=sdata.q;     // 取指针
           tag=sdata.tag; // 状态位
            if (tag==0)     //从左子树返回时,根的 tag=0
           {
                sdata.q=p;
                sdata.tag=1;      // 这时要进入根的右子树了,所以根的 tag=1 ,下次碰到根时就可以访问了
                Pushs(S,&sdata);   //(p,1) 进栈,根还得进一次栈
                p=p->rchild;     // 遍历右子树
           }
            else           //tag=1,这是说明了右子树访问完了返回,所以这次要对根进行访问了
           {
                printf( "%c\t" ,p->data);
                p=NULL;
           }
     }
}

二叉树的层次遍历
// 二叉树的层次遍历
void LevelTraverse(BiTree T)
{
     BiNode *p;
     LinkQueue *Q;
     InitQueue(Q);
     EnQueue(Q,T);
      while (!QueueEmpty(Q))
     {
           p=DeQueue(Q);
           printf( "%c\t" ,p->data);
            if (p->lchild!=NULL)
                EnQueue(Q,p->lchild);
            if (p->rchild!=NULL)
                EnQueue(Q,p->rchild);
     }
}

下面是完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "treenode.h"
#include "mystack.h"
#include "myqueue.h"
#include "newstack.h"
 
//按照先序序列输入构建一棵二叉树
void Create(BiTree &T)
{
      char ch;
     scanf( "%c",&ch);
      if( '#' == ch )
     {
           T = NULL;
     }
      else
     {
           T=(BiNode *)malloc( sizeof(BiNode));
           T->data=ch;
           Create(T->lchild);
           Create(T->rchild);
     }
}
 
//二叉树的先序遍历
void PreOrder(BiTree T)
{
      if(T)
     {
           printf( "%c ",T->data);
           PreOrder(T->lchild);
           PreOrder(T->rchild);
     }
}
 
//二叉树先序遍历的非递归算法
void PreTraverse(BiTree T)
{
     BiNode *p;
     Stack S;
     S=InitStack();
      if(T)
     {
           Push(S,T);  //根结点指针进栈
            while(!StackEmpty(S))
           {
                p=Pop(S);  //出栈,栈顶元素赋给p
                 while(p)
                {
                     printf( "%c\t",p->data);  // 访问p结点
                      if(p->rchild)
                           Push(S,p->rchild);  //右子树存在时,进栈
                     p=p->lchild;            //继续沿着左子树往下走
                }
           }
     }
}
 
//二叉树的中序遍历
void InOrder(BiTree T)
{
      if(T)
     {
           InOrder(T->lchild);
           printf( "%c ",T->data);
           InOrder(T->rchild);
     }
}
 
//二叉树的中序遍历的非递归算法
void InTraverse(BiTree T)
{
     BiNode *p;
     Stack S;
     S=InitStack();
     Push(S,T);  //根结点指针进栈
      while(!StackEmpty(S))
     {
            while((p=GetsTop(S))&&p)  //取栈顶元素且存在,赋给 p
                Push(S,p->lchild);    //p的左子树进栈
           p=Pop(S);               //去掉最后的空指针
            if(!StackEmpty(S))
           {
                p=Pop(S);       //弹出栈顶元素,赋给p
                printf( "%c\t",p->data);   // 访问p结点
                Push(S,p->rchild);     //右子树进栈,然后遍历右子树
           }
     }
}
 
//二叉树的后序遍历
void PostOrder(BiTree T)
{
      if(T)
     {
           PostOrder(T->lchild);
           PostOrder(T->rchild);
           printf( "%c ",T->data);
     }
}
 
void PostTraverse(BiTree T)
{
      int tag;
     BiNode *p;
     Stacks S;
     SNode sdata;
     S=InitStacks();
     p=T;
      while(p||!StacksEmpty(S))
     {
            while(p)
           {
                sdata.q=p;
                sdata.tag=0;
                Pushs(S,&sdata);   //(p,0)进栈
                p=p->lchild;      //遍历p 之左子树
           }
           sdata=*Pops(S);  //退栈
           p=sdata.q;     //取指针
           tag=sdata.tag; //状态位
            if(tag==0)     //从左子树返回时,根的 tag=0
           {
                sdata.q=p;
                sdata.tag=1;      //这时要进入根的右子树了,所以根的 tag=1,下次碰到根时就可以访问了
                Pushs(S,&sdata);   //(p,1)进栈,根还得进一次栈
                p=p->rchild;     //遍历右子树
           }
            else          //tag=1,这是说明了右子树访问完了返回,所以这次要对根进行访问了
           {
                printf( "%c\t",p->data);
                p=NULL;
           }
     }
}
 
//二叉树的层次遍历
void LevelTraverse(BiTree T)
{
     BiNode *p;
     LinkQueue *Q;
     InitQueue(Q);
     EnQueue(Q,T);
      while(!QueueEmpty(Q))
     {
           p=DeQueue(Q);
           printf( "%c\t",p->data);
            if(p->lchild!=NULL)
                EnQueue(Q,p->lchild);
            if(p->rchild!=NULL)
                EnQueue(Q,p->rchild);
     }
}
 
int main()
{
     BiTree T;
     Create(T);
     PostOrder(T);
     printf( "\n");
     LevelTraverse(T);
     printf( "\n");
     PostTraverse(T);
     printf( "\n");
 
      return 0;
}

/*测试数据*/
/* 分别是构建二叉树的输入数据以及先序、中序、后序、层次遍历序列:
ABE##C#D##F#GH#I##J##
ABECDFGHIJ
EBCDAFHIGJ
EDCBIGJGFA
ABFECGDHJI
 */

相关文章推荐

二叉树前序、中序、后序遍历的递归与非递归算法实现

//前序 递归 void preOrderRecursive(Tree * t) { if( !t ) return; visist(t); ...
  • kobep
  • kobep
  • 2013年04月24日 10:50
  • 381

二叉树中序遍历和后序遍历的递归与非递归算法

昨天写的前序遍历的递归与非递归算法,在非递归算法中主要还是借用到了栈这一工具,其实在中序遍历和后序遍历中依旧可以理由栈的特性来进行非递归的遍历 操作。 1.中序遍历 1.1 中序遍历的递归算法 二...

二叉树遍历的递归与非递归算法

二叉树遍历主要有四种:前序遍历,中序遍历,后序遍历,层序遍历。
  • Veahlin
  • Veahlin
  • 2017年02月25日 16:33
  • 155

C语言 二叉树的遍历 递归和(多种)非递归算法

//二叉树遍历 //作者:nuaazdh //时间:2011年12月1日 #include #include #define OK 1 #define ERROR...
  • nuaazdh
  • nuaazdh
  • 2012年05月31日 20:18
  • 16276

二叉树层序遍历的递归和非递归算法

二叉树层序遍历的递归和非递归算法

剑指offer面试题19二叉树的镜像和二叉树的前中后遍历递归和非递归算法

#include #include #include #include #include using namespace std; struct BinaryTreeNode { int d...
  • twlkyao
  • twlkyao
  • 2013年06月25日 09:57
  • 750

Java实现对二叉树前序/中序/后序的递归与非递归算法

二叉树的前序、中序、后序遍历的定义: 前序遍历:对任一子树,先访问跟,然后遍历其左子树,最后遍历其右子树; 中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树; 后序遍历:对任一...
  • novelly
  • novelly
  • 2013年05月15日 21:28
  • 550

【算法题】二叉树的前序遍历(递归和非递归算法分析)

问题:对二叉树的前序遍历(递归和非递归算法) 递归算法:输出根节点的值          对左子树进行左遍历          对右子树进行遍历   代码如下: void Preorde...

后序遍历该二叉树的非递归算法

  • 2010年07月13日 10:15
  • 78KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:二叉树的递归遍历与非递归算法实现
举报原因:
原因补充:

(最多只允许输入30个字)