二叉树的非递归遍历

二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的。对于二叉树,有前序、中序以及后序三种遍历方法。因为树的定义本身就 是递归定义,因此采用递归的方法去实现树的三种遍历不仅容易理解而且代码很简洁。而对于树的遍历若采用非递归的方法,就要采用栈去模拟实现。在三种遍历 中,前序和中序遍历的非递归算法都很容易实现,非递归后序遍历实现起来相对来说要难一点。

一.前序遍历

前序遍历按照“根结点-左孩子-右孩子”的顺序进行访问。

1.递归实现

void preOrder1(BinTree *root)     //递归前序遍历 
{
if(root!=NULL)
{
cout<<root->data<<"";
preOrder1(root->lchild);
preOrder1(root->rchild);
}
}

2.非递归实现

根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。即对于任一结点,其可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问其左子树时,再访问它的右子树。因此其处理过程如下:

对于任一结点P:

1)访问结点P,并将结点P入栈;

2)判断结点P的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,循环至1);若不为空,则将P的左孩子置为当前的结点P;

3)直到P为NULL并且栈为空,则遍历结束。

void preOrder2(BinTree *root)     //非递归前序遍历 
{
stack<BinTree*> s;
BinTree *p=root;
while(p!=NULL||!s.empty())
{
while(p!=NULL)
{
cout<<p->data<<"";
s.push(p);
p=p->lchild;
}
if(!s.empty())
{
p=s.top();
s.pop();
p=p->rchild;
}
}
}

二.中序遍历

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

1.递归实现

void inOrder1(BinTree *root)      //递归中序遍历
{
if(root!=NULL)
{
inOrder1(root->lchild);
cout<<root->data<<"";
inOrder1(root->rchild);
}
}

2.非递归实现

根据中序遍历的顺序,对于任一结点,优先访问其左孩子,而左孩子结点又可以看做一根结点,然后继续访问其左孩子结点,直到遇到左孩子结点为空的结点才进行访问,然后按相同的规则访问其右子树。因此其处理过程如下:

对于任一结点P,

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

2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子;

3)直到P为NULL并且栈为空则遍历结束

void inOrder2(BinTree *root)      //非递归中序遍历
{
stack<BinTree*> s;
BinTree *p=root;
while(p!=NULL||!s.empty())
{
while(p!=NULL)
{
s.push(p);
p=p->lchild;
}
if(!s.empty())
{
p=s.top();
cout<<p->data<<"";
s.pop();
p=p->rchild;
}
}
}

三.后序遍历

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

1.递归实现

void postOrder1(BinTree *root)    //递归后序遍历
{
if(root!=NULL)
{
postOrder1(root->lchild);
postOrder1(root->rchild);
cout<<root->data<<"";
}
}

2.非递归实现

后序遍历的非递归实现是三种遍历方式中最难的一种。因为在后序遍历中,要保证左孩子和右孩子都已被访问并且左孩子在右孩子前访问才能访问根结点,这就为流程的控制带来了难题。下面介绍两种思路。

第一种思路:对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,但是此时不能将其出栈并访问, 因此其右孩子还为被访问。所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在栈顶,此时可以将其出栈并访问。这样就 保证了正确的访问顺序。可以看出,在这个过程中,每个结点都两次出现在栈顶,只有在第二次出现在栈顶时,才能访问它。因此需要多设置一个变量标识该结点是 否是第一次出现在栈顶。

void postOrder2(BinTree *root)    //非递归后序遍历
{
stack<BTNode*> s;
BinTree *p=root;
BTNode *temp;
while(p!=NULL||!s.empty())
{
while(p!=NULL) //沿左子树一直往下搜索,直至出现没有左子树的结点
{
BTNode *btn=(BTNode *)malloc(sizeof(BTNode));
btn->btnode=p;
btn->isFirst=true;
s.push(btn);
p=p->lchild;
}
if(!s.empty())
{
temp=s.top();
s.pop();
if(temp->isFirst==true) //表示是第一次出现在栈顶
{
temp->isFirst=false;
s.push(temp);
p=temp->btnode->rchild;
}
else//第二次出现在栈顶
{
cout<<temp->btnode->data<<"";
p=NULL;
}
}
}
}

第二种思路:要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P 存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证 了每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。

void postOrder3(BinTree *root)     //非递归后序遍历
{
stack<BinTree*> s;
BinTree *cur; //当前结点
BinTree *pre=NULL; //前一次访问的结点
s.push(root);
while(!s.empty())
{
cur=s.top();
if((cur->lchild==NULL&&cur->rchild==NULL)||
(pre!=NULL&&(pre==cur->lchild||pre==cur->rchild)))
{
cout<<cur->data<<""; //如果当前结点没有孩子结点或者孩子节点都已被访问过
s.pop();
pre=cur;
}
else
{
if(cur->rchild!=NULL)
s.push(cur->rchild);
if(cur->lchild!=NULL)
s.push(cur->lchild);
}
}
}

四.整个程序完整的代码

/*二叉树的遍历* 2011.8.25*/ 

#include <iostream>
#include<string.h>
#include<stack>
usingnamespace std; typedef struct node
{
char data;
struct node *lchild,*rchild;
}BinTree; typedef struct node1
{
BinTree *btnode;
bool isFirst;
}BTNode; void creatBinTree(char*s,BinTree *&root) //创建二叉树,s为形如A(B,C(D,E))形式的字符串
{
int i;
bool isRight=false;
stack<BinTree*> s1; //存放结点
stack<char> s2; //存放分隔符
BinTree *p,*temp;
root->data=s[];
root->lchild=NULL;
root->rchild=NULL;
s1.push(root);
i=;
while(i<strlen(s))
{
if(s[i]=='(')
{
s2.push(s[i]);
isRight=false;
}
elseif(s[i]==',')
{
isRight=true;
}
elseif(s[i]==')')
{
s1.pop();
s2.pop();
}
elseif(isalpha(s[i]))
{
p=(BinTree *)malloc(sizeof(BinTree));
p->data=s[i];
p->lchild=NULL;
p->rchild=NULL;
temp=s1.top();
if(isRight==true)
{
temp->rchild=p;
cout<<temp->data<<"的右孩子是"<<s[i]<<endl;
}
else
{
temp->lchild=p;
cout<<temp->data<<"的左孩子是"<<s[i]<<endl;
}
if(s[i+]=='(')
s1.push(p);
}
i++;
}
} void display(BinTree *root) //显示树形结构
{
if(root!=NULL)
{
cout<<root->data;
if(root->lchild!=NULL)
{
cout<<'(';
display(root->lchild);
}
if(root->rchild!=NULL)
{
cout<<',';
display(root->rchild);
cout<<')';
}
}
} void preOrder1(BinTree *root) //递归前序遍历
{
if(root!=NULL)
{
cout<<root->data<<"";
preOrder1(root->lchild);
preOrder1(root->rchild);
}
} void inOrder1(BinTree *root) //递归中序遍历
{
if(root!=NULL)
{
inOrder1(root->lchild);
cout<<root->data<<"";
inOrder1(root->rchild);
}
} void postOrder1(BinTree *root) //递归后序遍历
{
if(root!=NULL)
{
postOrder1(root->lchild);
postOrder1(root->rchild);
cout<<root->data<<"";
}
} void preOrder2(BinTree *root) //非递归前序遍历
{
stack<BinTree*> s;
BinTree *p=root;
while(p!=NULL||!s.empty())
{
while(p!=NULL)
{
cout<<p->data<<"";
s.push(p);
p=p->lchild;
}
if(!s.empty())
{
p=s.top();
s.pop();
p=p->rchild;
}
}
} void inOrder2(BinTree *root) //非递归中序遍历
{
stack<BinTree*> s;
BinTree *p=root;
while(p!=NULL||!s.empty())
{
while(p!=NULL)
{
s.push(p);
p=p->lchild;
}
if(!s.empty())
{
p=s.top();
cout<<p->data<<"";
s.pop();
p=p->rchild;
}
}
} void postOrder2(BinTree *root) //非递归后序遍历
{
stack<BTNode*> s;
BinTree *p=root;
BTNode *temp;
while(p!=NULL||!s.empty())
{
while(p!=NULL) //沿左子树一直往下搜索,直至出现没有左子树的结点
{
BTNode *btn=(BTNode *)malloc(sizeof(BTNode));
btn->btnode=p;
btn->isFirst=true;
s.push(btn);
p=p->lchild;
}
if(!s.empty())
{
temp=s.top();
s.pop();
if(temp->isFirst==true) //表示是第一次出现在栈顶
{
temp->isFirst=false;
s.push(temp);
p=temp->btnode->rchild;
}
else//第二次出现在栈顶
{
cout<<temp->btnode->data<<"";
p=NULL;
}
}
}
} void postOrder3(BinTree *root) //非递归后序遍历
{
stack<BinTree*> s;
BinTree *cur; //当前结点
BinTree *pre=NULL; //前一次访问的结点
s.push(root);
while(!s.empty())
{
cur=s.top();
if((cur->lchild==NULL&&cur->rchild==NULL)||
(pre!=NULL&&(pre==cur->lchild||pre==cur->rchild)))
{
cout<<cur->data<<""; //如果当前结点没有孩子结点或者孩子节点都已被访问过
s.pop();
pre=cur;
}
else
{
if(cur->rchild!=NULL)
s.push(cur->rchild);
if(cur->lchild!=NULL)
s.push(cur->lchild);
}
}
} int main(int argc, char*argv[])
{
char s[];
while(scanf("%s",s)==)
{
BinTree *root=(BinTree *)malloc(sizeof(BinTree));
creatBinTree(s,root);
display(root);
cout<<endl;
preOrder2(root);
cout<<endl;
inOrder2(root);
cout<<endl;
postOrder2(root);
cout<<endl;
postOrder3(root);
cout<<endl;
}
return;
}
作者:海子

    

    

本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
分类: 数据结构
绿色通道: 好文要顶 关注我 收藏该文与我联系
14
0
(请您对文章做出评价)
posted on 2011-08-25 20:12 海 子 阅读(22859) 评论(19) 编辑 收藏

评论

#1楼

2012-07-10 23:00

zyqhi

不错的实现,学习了

#2楼

2012-07-22 10:31

fobdddf

很全,学习了

#3楼

2012-08-28 17:37

xn4545945

很详细呀!

#4楼

2012-09-05 21:10

logzh

谢谢!非递归后序遍历思路2很不错!

#5楼

2012-10-05 21:56

白手开始拼

我的跟作者的思路不太一样
先序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void preOrder(Node *p) //非递归
{
   if(!p) return;
   stack<Node*> s;
   Node *t;
   s.push(p);
   while(!s.empty())
   {
       t=s.top();
       printf("%d\n",t->data);
       s.pop();
       if(t->right) s.push(t->right);
       if(t->left) s.push(t->left);
    }
 }

中序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void inOrder(Node *p)
{
     if(!p)
         return;
     stack< pair<Node*,int> > s;
     Node *t;
     int unUsed;
     s.push(make_pair(p,1));
     while(!s.empty())
     {
         t=s.top().first;
         unUsed = s.top().second;
         s.pop();
         if(unUsed)
         {
               if(t->right)
                     s.push( make_pair(t->right,1) );
              s.push( make_pair(t,0) );
              if(t->left)
                     s.push( make_pair(t->left,1));
          }
          else printf("%d\n",t->data);
       }
}

后序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void postOrder(Node *p)
{
    if(!p) return;
    stack<pair<Node*,int> > s;
    Node *t;
    int unUsed;
    s.push(make_pair(p,1));
    while(!s.empty())
    {
        t=s.top().first;
        unUsed=s.top().second;
        s.pop();
        if(unUsed)
        {
            s.push(make_pair(t,0);
            if(t->right)
                s.push(make_pair(t->right,1));
            if(t->left)
                s.push(make_pair(t->left,1));
        }
        else printf("%d\n",t->data);
    }
}

#6楼

2013-03-12 16:59

whdugh

后序遍历第二种思路 不错 很好理解

#7楼

2013-03-29 14:31

tongailing

我也觉得博主后序遍历的第二种思路,写的挺好的。我自己琢磨了一晚上加上一个上午还没有搞得很清楚,并且中午午休的时候,还以为你的后序遍历写错了了!
多谢博主

#8楼[楼主]

2013-03-29 14:37

海 子

@tongailing
引用我也觉得博主后序遍历的第二种思路,写的挺好的。我自己琢磨了一晚上加上一个上午还没有搞得很清楚,并且中午午休的时候,还以为你的后序遍历写错了了!
多谢博主
呵呵,互相学习。

#9楼

2013-04-01 16:29

qwer075

博主的思路很好,写的也很清晰,最近正在弄这块的东西。
我最先想到的非递归思路跟5楼的差不多。

#10楼

2013-04-15 14:38

挡不住会飞的鸡

谢谢分享,正在看

#11楼

2013-06-19 15:49

水风

非递归前序遍历第四行应该是:while(p!=NULL&&!s.empty())
如果某一个节点没有右节点,这个程序就结束了。

#12楼

2013-08-01 21:25

dannyxch

if(!s.empty()) 这个具体的内容 包括s.empty 的程序 和括号内的东西是什么

#13楼

2013-09-03 11:07

JMLiu

根据楼主的preOrder我写了一个postOrder,跟大家分享

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
typedef struct _Node{
    int data;
    struct _Node *left;
    struct _Node *right;
} node;
 
void postOrder_Stack(node *root) {
    stack < node * > s;
    stack < int > s_data;
    node *p = root;
    while(p != NULL || !s.empty()) {
        while(p != NULL) {
            s_data.push(p -> data);
            s.push(p);
            p = p -> right;
        }
        p = s.top();
        s.pop();
        p = p -> left;
    }
    while(!s_data.empty()) {
        cout << s_data.top() << " ";
        s_data.pop();
    }
    return ;
}

#14楼

2013-10-09 12:15

hilbertdu

@水风
没错!

#15楼

2013-10-10 15:01

andy713

很不错,总算一遍就看懂了。多谢

#16楼

2013-10-10 17:19

opener

楼主的非递归后序遍历postOrder3是不是有点问题呀,如果传入的参数root为空的话会异常。

#17楼

2013-11-18 23:21

jihite

@opener
确实是,这时入栈时可以这样
if(root)
s.push(root);
这样就可以了。

ZT 二叉树的非递归遍历的更多相关文章

  1. K:二叉树的非递归遍历

    相关介绍:  二叉树的三种遍历方式(先序遍历,中序遍历,后序遍历)的非递归实现,虽然递归方式的实现较为简单且易于理解,但是由于递归方式的实现受其递归调用栈的深度的限制,当递归调用的深度超过限制的时候, ...

  2. [Alg] 二叉树的非递归遍历

    1. 非递归遍历二叉树算法 (使用stack) 以非递归方式对二叉树进行遍历的算法需要借助一个栈来存放访问过得节点. (1) 前序遍历 从整棵树的根节点开始,对于任意节点V,访问节点V并将节点V入栈, ...

  3. 二叉树的非递归遍历C++实现

    #include<iostream> #include<stdlib.h> #define maxsize 100 using namespace std; typedef s ...

  4. C++编程练习(17)----“二叉树非递归遍历的实现“

    二叉树的非递归遍历 最近看书上说道要掌握二叉树遍历的6种编写方式,之前只用递归方式编写过,这次就用非递归方式编写试一试. C++编程练习(8)----“二叉树的建立以及二叉树的三种遍历方式“(前序遍历 ...

  5. 数据结构二叉树的递归与非递归遍历之java,javascript,php实现可编译(1)java

    前一段时间,学习数据结构的各种算法,概念不难理解,只是被C++的指针给弄的犯糊涂,于是用java,web,javascript,分别去实现数据结构的各种算法. 二叉树的遍历,本分享只是以二叉树中的先序 ...

  6. 二叉树3种递归和非递归遍历(Java)

    import java.util.Stack; //二叉树3种递归和非递归遍历(Java) public class Traverse { /******************一二进制树的定义*** ...

  7. c/c++二叉树的创建与遍历(非递归遍历左右中,破坏树结构)

    二叉树的创建与遍历(非递归遍历左右中,破坏树结构) 创建 二叉树的递归3种遍历方式: 1,先中心,再左树,再右树 2,先左树,再中心,再右树 3,先左树,再右树,再中心 二叉树的非递归4种遍历方式: ...

  8. JAVA递归、非递归遍历二叉树(转)

    原文链接: JAVA递归.非递归遍历二叉树 import java.util.Stack; import java.util.HashMap; public class BinTree { priva ...

  9. 非递归遍历二叉树Java实现

    2018-10-03 20:16:53 非递归遍历二叉树是使用堆栈来进行保存,个人推荐使用双while结构,完全按照遍历顺序来进行堆栈的操作,当然在前序和后序的遍历过程中还有其他的压栈流程. 一.Bi ...

随机推荐

  1. .Net Actor 服务端开发框架,Newbe.Claptrap 项目周报 1 - 还没轮影,先用轮跑

    Newbe.Claptrap 项目周报 1,第一周代码写了一点.但主要还是考虑理论可行性. 第一次接触本框架的读者,可以先点击此处阅读本框架相关的基础理论和工作原理. 周报是啥? 成功的开源作品,离不 ...

  2. NodeJS之 Express框架 app.use(express.static)

    一 .设置静态文件目录 语法如下: app.use(express.static(_dirname + '/public')); //设置静态文件目录 注: 将静态文件目录设置为项目根目录 + ‘/p ...

  3. Sqlserver 备份

    Transact-SQL   语法规则 Transact-SQL   引用中的语法关系图使用下列规则. 大写    : Transact-SQL   关键字.   斜体    : Transact-S ...

  4. Jsp&Servlet入门级项目全程实录第6讲

    惯例广告一发,对于初学真,真的很有用www.java1234.com,去试试吧! 1.建立数据表及数据(略) 2.创建student model package com.java1234.model; ...

  5. [PHP] 重回基础(date函数和strtotime函数)

    date():格式化一个本地时间或者日期,当前时间 2016年5月13日 15:19:49 使用函数date(),输出当前是月份中的第几天,参数:String类型 d 例如:echo date(&qu ...

  6. 悟空模式-java设计模式

    目前已定义的java设计模式细分下来有二十余种,这篇博客主要是想从大家所熟知的孙悟空入手,阐述各个设计模式的概念和优缺点,以及他们之间的联系. 在下面介绍的每个设计模式里,都会有与西游记相关的具体案例 ...

  7. PHPCMS V9标签循环嵌套调用数据的方法

    PHPCMS V9的标签制作以灵活见长,可以自由DIY出个性的数据调用,对于制作有风格有创意的网站模板很好用,今天就介绍一个标签循环嵌套方法,可以实现对PC标签循环调用,代码如下: 在此文件里/php ...

  8. 移除button点击时的黑边

    input[type=submit], input[type=reset], input[type=button]{ outline:none; filter: chroma(color=#00000 ...

  9. Spring Boot—09通过Form提交的映射

    package com.sample.smartmap.controller; import org.springframework.beans.factory.annotation.Autowire ...

  10. Android开发时,那些相见恨晚的工具或网站!

    本文来我在知乎话题Android开发时你遇到过什么相见恨晚的工具或网站?下的回答! 在实际Android开发过程确实会有很多相见恨晚的工具或网站出现,下面是我自己的一些分享. 1.源码网站 https ...