<转载于  >>> >

二叉树是一种非常重要的数据结构,很多其他数据机构都是基于二叉树的基础演变过来的。二叉树有前、中、后三种遍历方式,因为树的本身就是用递归定义的,因此采用递归的方法实现三种遍历,不仅代码简洁且容易理解,但其开销也比较大,而若采用非递归方法实现三种遍历,则要用栈来模拟实现(递归也是用栈实现的)。下面先简要介绍三种遍历方式的递归实现,再详细介绍三种遍历方式的非递归实现。

一、三种遍历方式的递归实现(比较简单,这里不详细讲解)

1、先序遍历——按照“根节点-左孩子-右孩子”的顺序进行访问。

 1 void pre_traverse(BTree pTree)
 2 {
 3     if(pTree)
 4     {
 5         printf("%c ",pTree->data);
 6         if(pTree->pLchild)
 7             pre_traverse(pTree->pLchild);
 8         if(pTree->pRchild)
 9             pre_traverse(pTree->pRchild);
10     }
11 }

2、中序遍历——按照“左孩子-根节点-右孩子”的顺序进行访问。

 1 void in_traverse(BTree pTree)
 2 {
 3     if(pTree)
 4     {
 5         if(pTree->pLchild)
 6             in_traverse(pTree->pLchild);
 7         printf("%c ",pTree->data);
 8         if(pTree->pRchild)
 9             in_traverse(pTree->pRchild);
10     }
11 }

3、后序遍历——按照“左孩子-右孩子-根节点”的顺序进行访问。

 1 void beh_traverse(BTree pTree)
 2 {
 3     if(pTree)
 4     {
 5         if(pTree->pLchild)
 6             beh_traverse(pTree->pLchild);
 7         if(pTree->pRchild)
 8             beh_traverse(pTree->pRchild);
 9         printf("%c ",pTree->data);
10 }

二、三种遍历方式的非递归实现

为了便于理解,这里以下图的二叉树为例,分析二叉树的三种遍历方式的实现过程。

1、前序遍历的非递归实现 

根据先序遍历的顺序,先访问根节点,再访问左子树,后访问右子树,而对于每个子树来说,又按照同样的访问顺序进行遍历,上图的先序遍历顺序为:ABDECF。非递归的实现思路如下:

对于任一节点P,

1)输出节点P,然后将其入栈,再看P的左孩子是否为空;

2)若P的左孩子不为空,则置P的左孩子为当前节点,重复1)的操作;

3)若P的左孩子为空,则将栈顶节点出栈,但不输出,并将出栈节点的右孩子置为当前节点,看其是否为空;

4)若不为空,则循环至1)操作;

5)如果为空,则继续出栈,但不输出,同时将出栈节点的右孩子置为当前节点,看其是否为空,重复4)和5)操作;

6)直到当前节点P为NULL并且栈空,遍历结束。

下面以上图为例详细分析其先序遍历的非递归实现过程:

首先,从根节点A开始,根据操作1),输出A,并将其入栈,由于A的左孩子不为空,根据操作2),将B置为当前节点,再根据操作1),将B输出,并将其入栈,由于B的左孩子也不为空,根据操作2),将D置为当前节点,再根据操作1),输出D,并将其入栈,此时输出序列为ABD;

由于D的左孩子为空,根据操作3),将栈顶节点D出栈,但不输出,并将其右孩子置为当前节点;

由于D的右孩子为空,根据操作5),继续将栈顶节点B出栈,但不输出,并将其右孩子置为当前节点;

由于B的右孩子E不为空,根据操作1),输出E,并将其入栈,此时输出序列为:ABDE;

由于E的左孩子为空,根据操作3),将栈顶节点E出栈,但不输出,并将其右孩子置为当前节点;

由于E的右孩子为空,根据操作5),继续将栈顶节点A出栈,但不输出,并将其右孩子置为当前节点;

由于A的右孩子C不为空,根据操作1),输出C,并将其入栈,此时输出序列为:ABDEC;

由于A的左孩子F不为空,根据操作2),则将F置为当前节点,再根据操作1),输出F,并将其入栈,此时输出序列为:ABDECF;

由于F的左孩子为空,根据操作3),将栈顶节点F出栈,但不输出,并将其右孩子置为当前节点;

由于F的右孩子为空,根据操作5),继续将栈顶元素C出栈,但不输出,并将其右孩子置为当前节点;

此时栈空,且C的右孩子为NULL,因此遍历结束。

根据以上思路,前序遍历的非递归实现代码如下:

 1 void pre_traverse(BTree pTree)
 2 {
 3     PSTACK stack = create_stack();  //创建一个空栈
 4     BTree node_pop;                 //用来保存出栈节点
 5     BTree pCur = pTree;             //定义用来指向当前访问的节点的指针
 6
 7     //直到当前节点pCur为NULL且栈空时,循环结束
 8     while(pCur || !is_empty(stack))
 9     {
10         //从根节点开始,输出当前节点,并将其入栈,
11         //同时置其左孩子为当前节点,直至其没有左孩子,及当前节点为NULL
12         printf("%c ", pCur->data);
13         push_stack(stack,pCur);
14         pCur = pCur->pLchild;
15         //如果当前节点pCur为NULL且栈不空,则将栈顶节点出栈,
16         //同时置其右孩子为当前节点,循环判断,直至pCur不为空
17         while(!pCur && !is_empty(stack))
18         {
19             pCur = getTop(stack);
20             pop_stack(stack,&node_pop);
21             pCur = pCur->pRchild;
22         }
23     }
24 }

2、中序遍历的非递归实现

根据中序遍历的顺序,先访问左子树,再访问根节点,后访问右子树,而对于每个子树来说,又按照同样的访问顺序进行遍历,上图的中序遍历顺序为:DBEAFC。非递归的实现思路如下:

对于任一节点P,

1)若P的左孩子不为空,则将P入栈并将P的左孩子置为当前节点,然后再对当前节点进行相同的处理;

2)若P的左孩子为空,则输出P节点,而后将P的右孩子置为当前节点,看其是否为空;

3)若不为空,则重复1)和2)的操作;

4)若为空,则执行出栈操作,输出栈顶节点,并将出栈的节点的右孩子置为当前节点,看起是否为空,重复3)和4)的操作;

5)直到当前节点P为NULL并且栈为空,则遍历结束。

下面以上图为例详细分析其中序遍历的非递归实现过程:

首先,从根节点A开始,A的左孩子不为空,根据操作1)将A入栈,接着将B置为当前节点,B的左孩子也不为空,根据操作1),将B也入栈,接着将D置为当前节点,由于D的左子树为空,根据操作2),输出D;

由于D的右孩子也为空,根据操作4),执行出栈操作,将栈顶结点B出栈,并将B置为当前节点,此时输出序列为DB;

由于B的右孩子不为空,根据操作3),将其右孩子E置为当前节点,由于E的左孩子为空,根据操作1),输出E,此时输出序列为DBE;

由于E的右孩子为空,根据操作4),执行出栈操作,将栈顶节点A出栈,并将节点A置为当前节点,此时输出序列为DBEA;

此时栈为空,但当前节点A的右孩子并不为NULL,继续执行,由于A的右孩子不为空,根据操作3),将其右孩子C置为当前节点,由于C的左孩子不为空,根据操作1),将C入栈,将其左孩子F置为当前节点,由于F的左孩子为空,根据操作2),输出F,此时输出序列为:DBEAF;

由于F的右孩子也为空,根据操作4),执行出栈操作,将栈顶元素C出栈,并将其置为当前节点,此时的输出序列为:DBEAFC;

由于C的右孩子为NULL,且此时栈空,根据操作5),遍历结束。

根据以上思路,中序遍历的非递归实现代码如下:

 1 void in_traverse(BTree pTree)
 2 {
 3     PSTACK stack = create_stack();  //创建一个空栈
 4     BTree node_pop;                 //用来保存出栈节点
 5     BTree pCur = pTree;             //定义指向当前访问的节点的指针
 6
 7     //直到当前节点pCur为NULL且栈空时,循环结束
 8     while(pCur || !is_empty(stack))
 9     {
10         if(pCur->pLchild)
11         {
12             //如果pCur的左孩子不为空,则将其入栈,并置其左孩子为当前节点
13             push_stack(stack,pCur);
14             pCur = pCur->pLchild;
15         }
16         else
17         {
18             //如果pCur的左孩子为空,则输出pCur节点,并将其右孩子设为当前节点,看其是否为空
19             printf("%c ", pCur->data);
20             pCur = pCur->pRchild;
21             //如果为空,且栈不空,则将栈顶节点出栈,并输出该节点,
22             //同时将它的右孩子设为当前节点,继续判断,直到当前节点不为空
23             while(!pCur && !is_empty(stack))
24             {
25                 pCur = getTop(stack);
26                 printf("%c ",pCur->data);
27                 pop_stack(stack,&node_pop);
28                 pCur = pCur->pRchild;
29             }
30         }
31     }
32 }

3、后序遍历的非递归实现

根据后序遍历的顺序,先访问左子树,再访问右子树,后访问根节点,而对于每个子树来说,又按照同样的访问顺序进行遍历,上图的后序遍历顺序为:DEBFCA。后序遍历的非递归的实现相对来说要难一些,要保证根节点在左子树和右子树被访问后才能访问,思路如下:

对于任一节点P,

1)先将节点P入栈;

2)若P不存在左孩子和右孩子,或者P存在左孩子或右孩子,但左右孩子已经被输出,则可以直接输出节点P,并将其出栈,将出栈节点P标记为上一个输出的节点,再将此时的栈顶结点设为当前节点;

3)若不满足2)中的条件,则将P的右孩子和左孩子依次入栈,当前节点重新置为栈顶结点,之后重复操作2);

4)直到栈空,遍历结束。

下面以上图为例详细分析其后序遍历的非递归实现过程:

首先,设置两个指针:Cur指针指向当前访问的节点,它一直指向栈顶节点,每次出栈一个节点后,将其重新置为栈顶结点,Pre节点指向上一个访问的节点;

Cur首先指向根节点A,Pre先设为NULL,由于A存在左孩子和右孩子,根据操作3),先将右孩子C入栈,再将左孩子B入栈,Cur改为指向栈顶结点B;

由于B的也有左孩子和右孩子,根据操作3),将E、D依次入栈,Cur改为指向栈顶结点D;

由于D没有左孩子,也没有右孩子,根据操作2),直接输出D,并将其出栈,将Pre指向D,Cur指向栈顶结点E,此时输出序列为:D;

由于E也没有左右孩子,根据操作2),输出E,并将其出栈,将Pre指向E,Cur指向栈顶结点B,此时输出序列为:DE;

由于B的左右孩子已经被输出,即满足条件Pre==Cur->lchild或Pre==Cur->rchild,根据操作2),输出B,并将其出栈,将Pre指向B,Cur指向栈顶结点C,此时输出序列为:DEB;

由于C有左孩子,根据操作3),将其入栈,Cur指向栈顶节点F;

由于F没有左右孩子,根据操作2),输出F,并将其出栈,将Pre指向F,Cur指向栈顶结点C,此时输出序列为:DEBF;

由于C的左孩子已经被输出,即满足Pre==Cur->lchild,根据操作2),输出C,并将其出栈,将Pre指向C,Cur指向栈顶结点A,此时输出序列为:DEBFC;

由于A的左右孩子已经被输出,根据操作2),输出A,并将其出栈,此时输出序列为:DEBFCA;

此时栈空,遍历结束。

根据以上思路,后序遍历的非递归实现代码如下:

 1 void beh_traverse(BTree pTree)
 2 {
 3     PSTACK stack = create_stack();  //创建一个空栈
 4     BTree node_pop;          //用来保存出栈的节点
 5     BTree pCur;              //定义指针,指向当前节点
 6     BTree pPre = NULL;       //定义指针,指向上一各访问的节点
 7
 8     //先将树的根节点入栈
 9     push_stack(stack,pTree);
10     //直到栈空时,结束循环
11     while(!is_empty(stack))
12     {
13         pCur = getTop(stack);   //当前节点置为栈顶节点
14         if((pCur->pLchild==NULL && pCur->pRchild==NULL) ||
15             (pPre!=NULL && (pCur->pLchild==pPre || pCur->pRchild==pPre)))
16         {
17             //如果当前节点没有左右孩子,或者有左孩子或有孩子,但已经被访问输出,
18             //则直接输出该节点,将其出栈,将其设为上一个访问的节点
19             printf("%c ", pCur->data);
20             pop_stack(stack,&node_pop);
21             pPre = pCur;
22         }
23         else
24         {
25             //如果不满足上面两种情况,则将其右孩子左孩子依次入栈
26             if(pCur->pRchild != NULL)
27                 push_stack(stack,pCur->pRchild);
28             if(pCur->pLchild != NULL)
29                 push_stack(stack,pCur->pLchild);
30         }
31     }
32 }

以上遍历算法在VC上实现的输出结果如下:

二叉树的层序遍历:

二叉树的层序遍历的实现还是比较简单的,由于其层级的关系,很明显要用到队列来辅助实现,主要是从左向右,自上而下,依次将二叉树的各节点入队,这样便可以保证输出的顺序是层序排列的。下面是算法的实现思想:

先将树的根节点入队,

如果队列不空,则进入循环

{

将队首元素出队,并输出它;

如果该队首元素有左孩子,则将其左孩子入队;

如果该队首元素有右孩子,则将其右孩子入队

}

C语言代码如下:

 1 void LevelOrderTraverse(BiTree T,Status(*Visit)(TElemType))
 2 {
 3     //Visit是对节点操作的应用函数,
 4     //在这里,对每个数据元素调用函数Visit,也即是遍历了该节点
 5     SqQueue q;
 6     QElemType p;
 7     if(T)
 8     {
 9         InitQueue(&q);
10         EnQueue(&q,T);
11         while(!QueueEmpty(q))
12         {
13             DeQueue(&q,&p);
14             Visit(p->data);
15             if(p->lchild!=NULL) EnQueue(&q,p->lchild);
16             if(p->rchild!=NULL) EnQueue(&q,p->rchild);
17         }
18         printf("/n");
19     }
20 }

 

转载于:https://www.cnblogs.com/00isok/p/9871574.html

二叉树的三种遍历(递归与非递归) + 层次遍历相关推荐

  1. 实验五 二叉树的递归及非递归的遍历及其应用

    实验目的 熟练掌握二叉树的二叉链表存储结构的C语言实现.掌握二叉树的基本操作-前序.中序.后序遍历二叉树的三种方法.了解非递归遍历过程中"栈"的作用和状态,而且能灵活运用遍历算法实 ...

  2. C++实现二叉树 前、中、后序遍历(递归与非递归)非递归实现过程最简洁版本

    本文并非我所写,是复制的该链接中的内容: 最近学习二叉树,想编程实现递归和非递归的实现方式: 递归的方式就不说了,因为大家的递归程序都一样:但是对于非递归的实现方式, 根据这几天的查阅资料已看到差不多 ...

  3. 实现二叉树的三种非递归遍历算法

    [问题描述] 编写程序,实现二叉树的三种非递归遍历算法:先序非递归,中序非递归,后序非递归. [输入形式] 输入建树序列. [输出形式] 输出三种遍历序列. [样例输入] A B C # # # # ...

  4. 二叉树三种递归和非递归遍历 层序遍历

    以下为二叉树的三种遍历(中序,先序,后序)源码,和非递归遍历源码.  中序 递归遍历 public static void inorderTraversal(TreeNode root) {if(ro ...

  5. 二叉树的三种遍历方式(递归、非递归和Morris遍历)

    二叉树的三种遍历方式(递归.非递归和Morris遍历) 原文:http://www.linuxidc.com/Linux/2015-08/122480.htm 二叉树遍历是二叉树的最基本的操作,其实现 ...

  6. 二叉树的几种递归和非递归式遍历:

    二叉树的几种递归和非递归式遍历: 1 #include <fstream> 2 #include <iostream> 3 4 using namespace std; 5 6 ...

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

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

  8. 详解二叉树的三种遍历方式(递归、迭代、Morris算法)

    详解二叉树的三种遍历方式(递归.迭代.Morris算法) 最重要的事情写在前面:遍历顺序不一定就是操作顺序!!! 递归解法 首先,一颗二叉树它的递归序列是一定的,导致其前中后序不同的原因只不过是访问节 ...

  9. 二叉树的非递归遍历(递归和非递归)

    二 叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的.对于二叉树,有前序.中序以及后序三种遍历方法.因为树的定义本身就是 递归定义,因此采用递归的方法去实现树的三种遍历不仅 ...

最新文章

  1. stackoverflow_StackOverflow帐户如何确保您在公认的开发人员表格中占有一席之地
  2. VC++制作DLL具体解释
  3. YFIOServer 后台IO接口使用说明
  4. wordpress搭建博客 主题推荐 2019
  5. SQL Server 2008 R2 Developer Edition图文安装教程
  6. Java基础-Java中的内存分配与回收机制
  7. 32. 脱壳篇-简单带壳的程序、反调试带壳的程序(堆栈平衡原理找OEP、代码段设置断点)
  8. android 列表上拉加载更多,Android 下拉刷新,上拉加载更多控件–支持ListView,GridView和ScrollView...
  9. 正则表达式 数字和小数点_《自然语言处理综论》第三版笔记(二)之正则表达式,文本标准化和编辑距离...
  10. Zerotier Moon服务器配置
  11. linux c 静态连接,Linux cmake 静态链接boost
  12. 本科生毕业论文中期检查表
  13. could not extract ResultSet 错误
  14. (FPGA)verilog驱动RGB接口TFT液晶屏
  15. 【洛谷 4556】雨天的尾巴
  16. VUE微信H5生成二维码海报保存在本地相册
  17. PC端生成小程序二维码海报并下载
  18. 对接第三方顺丰丰桥下单Api接口实战教程java
  19. java音视频开发技术_FFmpeg音视频核心技术精讲与实战
  20. H5判断手机是否安装某个APP

热门文章

  1. mac 怎样卸载mysql_如何卸载mac中的mysql?
  2. Exynos4412 Uboot 移植(五)—— Uboot 移植过程
  3. 静态库和动态库的分析
  4. Memcached缓存在.Net 中的使用(memcacheddotnet)
  5. 提示缺少Qedit.h问题
  6. 文件逆顺输出到新文件(三种方案)
  7. [react] 你阅读了几遍React的源码?都有哪些收获?你是怎么阅读的?
  8. 前端学习(2937):vue对象之间的实例属性
  9. [html] IE6文字溢出BUG(别名:多出来的猪、谍影重重)怎么解决呢?
  10. [html] 跨域通信有哪些方式?