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

原创 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
 */

史上最简明易懂非递归遍历二叉树算法

三种不同的遍历方式区别在于栈空间的释放时机和输出结点信息时机的不同:先序和中序遍历是在访问栈顶元素的右孩子(右子树)之前退栈,而后序遍历在访问右子树之后退栈;先序遍历是在某结点入栈时输出其信息,而中序...
  • QiaoRuoZhuo
  • QiaoRuoZhuo
  • 2014年10月29日 14:59
  • 3686

【数据结构与算法】二叉树递归与非递归遍历(附完整源码)

二叉树是一种非常重要的数据结构,很多其他数据机构都是基于二叉树的基础演变过来的。二叉树有前、中、后三种遍历方式,因为树的本身就是用递归定义的,因此采用递归的方法实现三种遍历,不仅代码简洁且容易理解,但...
  • mmc_maodun
  • mmc_maodun
  • 2013年10月24日 08:58
  • 40512

对于二叉树三种非递归遍历方式的理解

利用栈实现二叉树的先序,中序,后序遍历的非递归操作 栈是一种先进后出的数据结构,其本质应是记录作用,支撑回溯(即按原路线返回);因此,基于其的二叉树遍历操作深刻的体现了其特性: 1.先入、后出,只...
  • sdulibh
  • sdulibh
  • 2016年01月24日 11:25
  • 1260

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

二叉树遍历的非递归实现 相对于递归遍历二叉树,非递归遍历显得复杂了许多,但换来的好处是算法的时间效率有了提高。下面对于我学习非递归遍历二叉树算法的过程进行总结为了便于理解,这里以下图的二叉树为例,分析...
  • kelvinmao
  • kelvinmao
  • 2016年05月15日 11:43
  • 4507

二叉树的四种遍历的递归和非递归的实现

二叉树的三种遍历为:前序遍历,中序遍历和后序遍历。 遍历的实现可分为递归和非递归。递归法与二叉树的定义相似,非递归法采用栈去模拟实现。 一、前序遍历的次序为:根结点——左结点——右结点。 递归法实现:...
  • xiaominkong123
  • xiaominkong123
  • 2016年06月02日 16:50
  • 408

二叉树三种遍历算法递归和非递归实现

//二叉树 三种遍历方式的 递归非递归实现 #include #include using namespace std; typedef struct BiTreeNode {     int...
  • w616589292
  • w616589292
  • 2016年02月28日 10:49
  • 977

数据结构-----中序遍历二叉树非递归算法(利用堆栈实现)

一、非递归中序遍历算法思想
  • u011459278
  • u011459278
  • 2014年04月19日 18:03
  • 3147

实现二叉树的先序遍历、中序遍历、后序遍历的递归非递归算法以及层次遍历算法

#include"iostream" #define maxsize 50 using namespace std; class node{ private: char data; node* l...
  • u012651730
  • u012651730
  • 2013年11月19日 22:42
  • 1291

经典算法学习——非递归遍历二叉树

我们知道二叉树是一种递归定义的数据结构,包括二叉树的创建、遍历、求树高、叶子节点个数等等。使用递归来进行以上操作非常的简便,相关实现请参考 《C语言实现二叉树的基本操作》。但是今天我们剑走偏锋,使用非...
  • CHENYUFENG1991
  • CHENYUFENG1991
  • 2016年10月03日 12:28
  • 2277

二叉树的递归和非递归方式的三种遍历

二叉树的三种遍历方式,前序遍历,中序遍历,后序遍历,中的前中后都是指的是根节点的访问顺序,这三种遍历方式的概念在这里就不多说了,太普遍了! 二叉树的建立 我们这里以前序遍历为例: 我们先定...
  • woshinannan741
  • woshinannan741
  • 2016年10月16日 13:18
  • 709
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:二叉树的递归遍历与非递归算法实现
举报原因:
原因补充:

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