二叉树的非递归遍历及算法分析

二叉树介绍

二叉树是一类重要的数据结构。二叉树常被用于实现二叉查找树和二叉堆。通常子树被称作“左子树”(left
subtree)和“右子树”(right
subtree)。

一种二叉树结点定义:

struct bit_node

{

chardata;

structbit_node *lchild,*rchild;

};

遍历是对树的一种最基本的运算,所谓遍历二叉树,就是按一定的规则和顺序走遍二叉树的所有结点,使每一个结点都被访问一次,而且只被访问一次。由于二叉树是非线性结构,因此,树的遍历实质上是将二叉树的各个结点转换成为一个线性序列来表示。

设L、D、R分别表示遍历左子树、访问根结点和遍历右子树,则对一棵二叉树的遍历有三种情况:DLR(称为先根次序遍历),LDR(称为中根次序遍历),LRD (称为后根次序遍历)。

递归方法遍历:

二叉树本身是一种递归定义的数据结构,所以递归遍历的方法是简单明了的方法。

DLR(先根次序遍历)

void traverse_bitree_dlr(struct bit_node*node)

{

if(node!= NULL)

{

printf("%3c",node->data);

traverse_bitree_dlr(node->lchild);

traverse_bitree_dlr(node->rchild);

}

}

LDR(中根次序遍历)

void traverse_bitree_ldr(struct bit_node*node)

{

if(node!= NULL)

{

traverse_bitree_ldr(node->lchild);

printf("%3c",node->data);

traverse_bitree_ldr(node->rchild);

}

}

LRD(后根次序遍历)

void traverse_bitree_lrd(struct bit_node*node)

{

if(node!= NULL)

{

traverse_bitree_lrd(node->lchild);

traverse_bitree_lrd(node->rchild);

printf("%3c",node->data);

}

}

使用递归遍历的优点是算法简单明了,缺点也十分明显:对于栈的消耗比较大。尤其是在嵌入式应用中,嵌入式处理器资源往往有限。每次递归调用,都会涉及到通用寄存器、SP指针、PC指针等的压栈。当树的深度比较大时,对于栈的消耗会变得非常严重,很有可能造成栈的溢出。

非递归方法遍历:

因此,二叉树的非递归遍历方法就显得非常有实际应用价值。下面是非递归遍历的算法,这里使用了数据结构栈,利用其先进后出的特点,用结点入栈出栈过程手工模拟递归调用过程中的栈操作。

前序遍历

思想:使用数据结构栈,1保存结点左侧分支上的所有结点,并遍历结点;2出站时,切换至右子树;3重复1、2。

void traverse_bitree_dlr_nr(struct bit_node*root)

{

structbit_node *p;

structbit_node *stack[100];

inti = -1;

p= root;

while(p!= NULL || i != -1)

{

/*从当前结点开始搜索左子树,直至为空,并将分支上的左子树入栈*/

while(p!= NULL)

{

printf("%3c",p->data);    /*先入根结点再入左子树,入栈前遍历,前序*/

stack[++i]= p;

p= p->lchild;

}

if(i!= -1)

{

p= stack[i--];                       /*取出栈顶结点*/

p= p->rchild;                      /*切换至右子树*/

}

}

}

中序遍历

思想:使用数据结构栈,1保存结点左侧分支上的所有结点;2出站时遍历结点,切换至右子树;3重复1、2。

相对于前序遍历,中序遍历要求只是将左子树和根结点的顺序对调了一下,这不正符合栈的特点吗?!后进先出本身就是一个顺序反转过程。入栈时遍历,就是前序,出栈时遍历不就是中序遍历吗?!

void traverse_bitree_ldr_nr(struct bit_node*root)

{

structbit_node *p;

structbit_node *stack[100];

inti = -1;

p= root;

while(p!= NULL || i != -1)

{

/*从当前结点开始搜索左子树,直至为空,并将分支上的左子树入栈*/

while(p!= NULL)

{

stack[++i]= p;

p= p->lchild;

}

if(i!= -1)

{

p= stack[i--];                       /*取出栈顶结点*/

printf("%3c",p->data);    /*先出左子树,再出根结点,出栈后遍历,中序*/

p= p->rchild;                      /*切换至右子树*/

}

}

}

后序遍历

后序遍历是三种遍历中最复杂的。

第一种思想:

和前序、中序思想一样,使用栈手工模拟递归调用过程中栈操作过程。

后序遍历要求根结点在左右子树均遍历完成后才能遍历,更确切的条件就是,根结点要在其右子树遍历完成后紧接着遍历。这样就需要保存上一次遍历的结点信息。再者,栈中保存了未遍历的树的全部信息。后序遍历要求:先左子树,再右子树最后根结点,所以在右子树没有遍历完之前,根结点不能出栈。这一点其实也是递归后序遍历中所存在的过程。在具体代码中,相对于前序和中序又存在一个:右子树没有遍历时,结点需再次入栈的过程

出栈时如果满足为叶子节点,或者该结点的右孩子已经遍历,则遍历。

具体代码如下:

void traverse_bitree_lrd_nr(struct bit_node*root)

{

structbit_node *cur, *pre;

structbit_node *stack[100];

inti = -1;

cur= root;

pre= NULL;

while(cur!= NULL || i != -1)

{

/*从当前结点开始搜索左子树,直至为空,并将分支上的左子树入栈*/

while(cur!= NULL)

{

stack[++i]= cur;

cur= cur->lchild;

}

if(i!= -1)

{

cur= stack[i--];                   /*取出栈顶结点*/

if(cur->rchild== NULL)     /*当前结点不存在右子树*/

{

pre= cur;                   /*记录本次遍历的结点*/

printf("%3c",cur->data);

cur= NULL;                         /*该结点已经遍历,为下一次从栈中取数据做准备*/

}

else                                       /*当前结点存在右子树*/

{

if(cur->rchild == pre)        /*如果该结点的右子树已经遍历,则遍历该结点*/

{

pre= cur;          /*记录本次遍历的结点*/

printf("%3c",cur->data);

cur= NULL;                /*该结点已经遍历,为下一次从栈中取数据做准备*/

}

else                              /*如果该结点的右子树没有遍历,则结点入栈,并切换至右子树*/

{

stack[++i]= cur;

cur= cur->rchild;

}

}

}

}

}

第二种思想:

完全利用栈的先进后出的特点,对于任意结点,先入栈右子树,再入栈左子树。出栈时如果满足为叶子节点,该结点的左孩子(只有左孩子)刚刚遍历过或者右孩子刚刚遍历,则遍历。这样就保证了遍历时先左子树,再右子树,最后根结点的顺序。

void traverse_bitree_lrd_nr_second(structbit_node *root)

{

structbit_node *cur, *pre = NULL;

structbit_node *stack[100];

inti = -1;

stack[++i]= root;

while(i != -1)

{

cur= stack[i];

if((cur->lchild == NULL && cur->rchild == NULL) || (pre != NULL&& (pre == cur->lchild || pre == cur->rchild)))

{

printf("%3c",cur->data);

i--;

pre= cur;

}

else

{

if(cur->rchild != NULL)

{

stack[++i]= cur->rchild;

}

if(cur->lchild != NULL)

{

stack[++i]= cur->lchild;

}

}

}

}

时间: 2024-10-11 06:02:20

二叉树的非递归遍历及算法分析的相关文章

二叉树的非递归遍历--京东2015笔试回忆

题目回忆: C/C++研发试卷:偏重于数据结构的考察,编程题有2题+1题附加题: 1.输入整数n,求m,m>9,m中各个数位的乘积=n的最小整数;如n=36,m=49; 2.二叉树前序遍历的非递归实现(本文的总结) 3.求第n个数,这个序列满足(2^i)*(3^j)*(5^k),前7个为:2,3,4,5,6,8,10 .... 小题有基本的数据结构.程序运行结果.SQL题目. 4.删除表格用DROP命令,死锁产生的条件: 4.1互斥使用(资源独占) 一个资源每次只能给一个进程使用 4.2.不可强

好久没更新了,哪里不对!更新二叉树的非递归遍历

#include <iostream> #include <stack> using namespace std; typedef struct Node { Node* lchild; Node* rchild; int data; }BNode,BTree; void visit(Node*); void inorder(BTree *root) { BNode * p = root; stack<Node*> s; if(p||!s.empty()) { s.pu

二叉树的非递归遍历(转)

原文地址 二叉树的非递归遍历 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的.对于二叉树,有前序.中序以及后序三种遍历方法.因为树的定义本身就是递归定义,因此采用递归的方法去实现树的三种遍历不仅容易理解而且代码很简洁.而对于树的遍历若采用非递归的方法,就要采用栈去模拟实现.在三种遍历中,前序和中序遍历的非递归算法都很容易实现,非递归后序遍历实现起来相对来说要难一点. 一.前序遍历 前序遍历按照“根结点-左孩子-右孩子”的顺序进行访问. 1.递归实现 void pr

二叉树的非递归遍历C语言实现

腾讯面试中被问到二叉树的非递归遍历实现,当时记得不太清楚,回来专门复习了非递归的实现,整理代码如下: //采用二叉链表存储方式的二叉树,非递归中序遍历C语言实现代码 #include<stdio.h> #include <malloc.h> //函数结果状态代码 #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFEASIBLE -1 #define OVERFLOW -2 //Status是

(转)二叉树的非递归遍历

转自: 二叉树的非递归遍历 http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html 二叉树的非递归遍历 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的.对于二叉树,有前序.中序以及后序三种遍历方法.因为树的定义本身就是递归定义,因此采用递归的方法去实现树的三种遍历不仅容易理解而且代码很简洁.而对于树的遍历若采用非递归的方法,就要采用栈去模拟实现.在三种遍历中,前序和中序遍历的非递归算法都

二叉树的非递归遍历(转载)

二叉树的非递归遍历 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的.对于二叉树,有前序.中序以及后序三种遍历方法.因为树的定义本身就是递归定义,因此采用递归的方法去实现树的三种遍历不仅容易理解而且代码很简洁.而对于树的遍历若采用非递归的方法,就要采用栈去模拟实现.在三种遍历中,前序和中序遍历的非递归算法都很容易实现,非递归后序遍历实现起来相对来说要难一点. 一.前序遍历 前序遍历按照“根结点-左孩子-右孩子”的顺序进行访问. 1.递归实现 void preOrde

二叉树的非递归遍历(先序、中序、后序和层序遍历)

[前文] 二叉树的非递归遍历有 先序遍历.中序遍历 .后续遍历 和 层序遍历. 非递归算法实现的基本思路:使用堆栈.而层序遍历的实现:使用队列. 如下图所示的二叉树: 前序遍历顺序为:ABCDE (先访问根节点,然后先序遍历其左子树,最后先序遍历其右子树) 中序遍历顺序为:CBDAE (先中序遍历其左子树,然后访问很节点,最后中序遍历其右子树) 后续遍历顺序为:CDBEA (先后序遍历其左子树,然后后续其右子树,最后访问根节点) 层序遍历顺序为:ABECD (由上至下.从左到右遍历二叉树) [准

二叉树之非递归遍历

1.二叉树的遍历 为什么要有遍历操作:将线性结构-------->非线性结构: 将递归程序-------->非递归程序: 2.二叉树的三种递归遍历: 先序遍历:先访问根(父)结点,在访问左分支,最后访问右分支: 中序遍历:先访问左分支,在根结点,最后右分支: 后序遍历:先访问左分支,在访问右分支,最后访问根节点: 所有程序皆正确测试过,后面将给完整程序和测试程序,测试结果. 以下就是递归遍历,先序,中序,后序: 下面的都是在类外定义的函数,所以为模板函数: //先序遍历 template<

[算法]二叉树的非递归遍历算法

1.二叉树的非递归中序遍历算法 二叉树的中序遍历方法是:左中右,因此一开始会顺着根节点的左孩子一直往下(这点和先序遍历一样,这也是二者前面部分代码很相似的原因),到最后一个左孩子时尝试把它的右孩子塞进栈内,然后顺着它的的左孩子而下,直到不能访问为止.利用的栈FILO的特性,对每个节点都进行顺左孩子而下即可. 上代码: 1 void inOrder(TreeNode* root,vector<int>& inOrder) 2 { 3 stack<TreeNode*>st; 4