二叉树三种遍历方式的递归和循环实现

限时加码!20+主流AI编程工具免费用 购周边加赠Coding Plan Lite,Claude Code、Cursor等即刻畅享,学习进阶更高效! 阅读详情

转载自:http://blog.csdn.net/pi9nc/article/details/13008511

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


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


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

[cpp]  view plain copy
  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、中序遍历——按照“左孩子-根节点-右孩子”的顺序进行访问。

[cpp]  view plain copy
  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、后序遍历——按照“左孩子-右孩子-根节点”的顺序进行访问。

[cpp]  view plain copy
  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)直到当前节点PNULL并且栈空,遍历结束。

   

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

首先,从根节点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,因此遍历结束。


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

[cpp]  view plain copy
  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)直到当前节点PNULL并且栈为空,则遍历结束。


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

首先,从根节点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),遍历结束。


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

[cpp]  view plain copy
  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首先指向根节点APre先设为NULL,由于A存在左孩子和右孩子,根据操作3),先将右孩子C入栈,再将左孩子B入栈,Cur改为指向栈顶结点B

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

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

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

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

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

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

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

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

此时栈空,遍历结束。


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

[cpp]  view plain copy
  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上实现的输出结果如下:

问题 A: DS二叉树--层次遍历 题目描述 层次遍历二叉树,是从根结点开始遍历,按层次次序“自上而下,从左至右”访问树中的各结点。 建树方法采用“先序遍历+空树用0表示”的方法 要求:采用队列对象实现,函数框架如下: 输入 第一行输入一个整数t,表示有t个测试数据 第二行起输入二叉树先序遍历结果,空树用字符‘0’表示,输入t行 输出 逐行输出每个二叉树的层次遍历结果 样例输入 2 AB0C00D00 ABCD00E000FG00H0I00 样例输出 ABDC ABFCGHDEI 提示 ... 阅读详情

相关推荐

树的三种递归遍历二叉树

树的非递归遍历是指使用栈(Stack)来模拟递归过程,从而实现树的遍历。以下是三种常见的非递归遍历算法:前序遍历(Pre-order)、中序遍历(In-order)后序遍历(Post-order)的详细实现,包括原理、步骤、图示法表示步骤代码关键行注释。以下是一个完整的示例程序,演示如何使用非递归方法进行前序、中序后序遍历。前序遍历的顺序是:根节点 -> 左子树 -> 右子树。中序遍历的顺序是:左子树 -> 根节点 -> 右子树。后序遍历的顺序是:左子树 -> 右子树 -> 根节点。

m0_49844997的博客 1227

二叉树的建立层次遍历

实验三 二叉树的建立层次遍历 一 实验目的 掌握二叉树的原理、表示及实现方法。 掌握二叉树的基本操作,二叉树的建立、遍历方法。 二 实验要求 认真设计本实验的程序,上机调试运行本程序。 保存或打印出程序的运行结果记录程序调试过程中出现的问题,对问题加以分析。 三 实验内容 按先序遍历序列建立链式存储结构的二叉树。 对该二叉树按层次进行遍历,并输出结果。 选作实验:对二叉树按中序或后序进行遍历 实验时间:第10周

C++实现递归,非递归遍历二叉树(前序,中序,后序)

初学二叉树那会儿,始终掌握不好二叉树遍历方法,更认为非递归遍历晦涩难懂没有掌握的意义。实际上非递归遍历方法很有用处,由于每次递归都需要将函数的信息入栈,当递归层数太深很容易就导致栈溢出,所以这个时候就必须用到非递归遍历二叉树了。而且,当看懂非递归遍历后,你会发现,其实非递归也很简单。 我们知道,要想处理二叉树,离不开其遍历方式遍历二叉树三种方式,前序遍历,中序遍历,后序遍历,每种...

Hairy_Monsters的博客 4160

数据结构课程设计二叉树采用二叉链表作为存储结构

编写按层次顺序(一层自左至右)遍历二叉树的算法。 (1二叉树采用二叉链表作为存储结构。 (2)按题集p44面题6.69所指定的格式输出建立的二叉树。 (3输出层次遍历结果。 (4)测试用例自己设计。

详细讲解二叉树三种遍历方式递归与非递归实现

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

473687880 3938

数据结构 — 5.二叉树层次遍历

【问题描述】编写按层次顺序(一层自左至右)遍历二叉树的算法. 【输入形式】A B * C * * D * *  ( 按照先序输入二叉树中结点的值、字符在输入中没有空格 ) 【输出形式】A B D C #include #include #include using namespace std; typedef struct BiTNode{ char data; B

PeersLee的博客 1207

二叉树三种遍历方式循环递归实现方式

///////////////////头文件:BST.h//////////////////////// #ifndef BST_H #define BST_H #include "StdAfx.h" #include #include template class BST { public: class Node { public:

我与大象的故事 377

基于Java实现二叉树三种遍历每,每种遍历都采用递归循环两种方式实现

先序遍历: “根左右”,遍历的顺序: 根节点->左节点->右节点。中序遍历: “左根右”,遍历的顺序: 左节点->根节点->右节点。后序遍历: “左右根”,遍历的顺序: 左节点->右节点->根节点。三种遍历方式,也就是遍历的顺序不一样。

qq_44182424的博客 1290

二叉树递归与非递归遍历

二叉树遍历 常见的二叉树遍历三种方式:先序遍历(根结点,左子树,右子树) 中序遍历(左子树,根结点,右子树)    后续遍历(左子树,右子树,根结点) 这三种方式有分别可以用递归递归实现 先序遍历的主要步骤:(参考下图) 1.从根结点开始先压左路结点,并访问结点,直到把根结点左路结点全部压入栈。 2.若左子树为空,说明左路根结点已经全部压栈并且已经访问过了,开始取栈顶元素来访问上一层父节点的右子树。把 右子树看成子问题继续进行1步骤。 3.依次进行上述12

TerryZjl的博客 1651

用c语言实现二叉树三种遍历_利用循环递归实现二叉树三种遍历

一、前序遍历遍历的过程为:首先访问根节点,再前序访问其左子树,再前序访问其右子树。输入二叉树:正确答案为:[1,3,4,43,3,2,2,2,42]递归实现:void 输出:[1,3,4,43,3,2,2,2,42]循环实现:vector输出:[1,3,4,43,3,2,2,2,42]二、中序遍历遍历过程为:首先中序遍历其左子树,访问根结点,中序遍历其右子树,代码如下:递归实现:void 循环实现...

weixin_39605578的博客 402

二叉树的创建遍历

最近找实习,觉得需要记录一下自己学习的内容。第一次写博客,但愿有个好的开始。 前天去华为面试,面了50分钟,最后败倒在一棵树下(当时遍历反了,从root往子节点遍历,其实是从子节点向上遍历),好傻,都怪自己平时写代码比较少,代码这种东西,还是得多练。 二叉树:如果只有一个节点,那么这个节点就是根节点;如果不只一个节点,那么它的左子树右子树都是一棵二叉树。这是二叉树递归定义。 二叉树的建立

u010465615的博客 250

【C++】【LeetCode】【二叉树的前序、中序、后序遍历】【递归+非递归

默认情况下,一个线程的栈空间大小为8MB,当递归的深度太深,我们的程序就容易崩溃如果递归的深度太深,栈空间不大,那么程序容易崩溃改成非递归1.改成循环(比方说斐波那契)2.通过栈改成循环为什么递归会空间溢出,非递归不会空间溢出?1.如果是循环的话,形成迭代,消耗的空间很可能是0(1)2.如果使用的是通过栈来改成循环的话,我们栈的开辟的空间在内存的堆区(内存中栈的空间不大,但是堆的空间很大,几乎不存在空间溢出的问题)所以这里我们将递归遍历改成非递归

芊樱烛渊的博客 856

二叉树三种遍历方式递归、栈、循环 分类: C/C++ ...

三种方法中,递归最为简单,栈次之,循环最为麻烦。递归的深度如果太大则会导致栈溢出;栈的方式需要额外的辅助空间;循环编程最麻烦。 首先是递归: //递归方法 void midPrint_r(TreeNode* root) {//中序遍历 if(root==NULL) return; if(root->left) midPrin...

weixin_30445169的博客 214

二叉树三种遍历:前序遍历、后序遍历、中序遍历

题目链接:145.二叉树的后序遍历 94.二叉树的中序遍历 144.二叉树的前序遍历 1、后序遍历 主要想说这个题目N叉树一样,用了一个迭代的方法去解决。二叉树的后续是左、右、根。利用栈的数据结构,进行根、右、左的遍历。然后把最终遍历结果进行反转就可以了。 # Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x #

奥古斯都 558

重生之我在代码随想录刷算法第十一天 | 二叉树递归遍历二叉树的迭代遍历二叉树的层序遍历

60天算法训练营第11天,今天登场的是二叉树的各种遍历!

qq_73210658的博客 463

【代码随想录】——“二叉树递归遍历

每次写递归,都按照这三要素来写确定递归函数的参数返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。 144. 二叉树

m0_51660741的博客 291

如何在不使用递归的情况下遍历一颗二叉树

面试的时候,往往面试官会强制你写出非递归的版本,网上关于非递归版本的介绍也有很多,这里我分享一个自己认为是比较好记的版本。 显然,我们需要用一个stack来模拟递归时的函数调用。对于三种遍历,我们都使用push当前节点->push左子树->pop左子树->push右子树->pop右子树的方式。但是cout时机会有所不。 对于前序遍历来说,每次访问到一个节点就cout; 对于中序遍历来说,每次将右子节点进栈时,把当前节点cout; 对于后序遍历来说,每次pop的时候cout。 使用工

weixin_43907316的博客 422
上一篇: 蚂蚁爬行问题及其改编
下一篇: vs的debug和release版本的区别
Lieacui
博客等级 码龄12年 4粉丝 8原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值