当前位置: 首页 > news >正文

二叉树的非递归遍历

 

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

一.前序遍历

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

   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);
}
}
}
复制代码

 四.整个程序完整的代码

按 Ctrl+C 复制代码
/*二叉树的遍历* 2011.8.25*/ #include <iostream> #include<string.h> #include<stack> using namespace 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[0]; root->lchild=NULL; root->rchild=NULL; s1.push(root); i=1; while(i<strlen(s)) { if(s[i]=='(') { s2.push(s[i]); isRight=false; } else if(s[i]==',') { isRight=true; } else if(s[i]==')') { s1.pop(); s2.pop(); } else if(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+1]=='(') 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[100]; while(scanf("%s",s)==1) { 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 0; }
按 Ctrl+C 复制代码

 

转载于:https://www.cnblogs.com/xulb597/archive/2012/05/12/2497264.html

相关文章:

  • 如果在IIS中没有将虚拟目录配置为应用程序,则可能导致此错误
  • COJ1150(食用油)
  • Tomcat jdk配置及内存设置
  • 复习笔记
  • [转]Web前端研发工程师编程能力飞升之路
  • oracle合并记录的用法merge
  • 使用武器CALL
  • VB.NET Frm的hide close dispose
  • SetWindowRgn文字窗体
  • redis内存示意图
  • Spring最佳实践-9.1 集成邮件服务
  • java 并行 用happen-before规划重新审视DCL
  • 排序规则在全角与半角处理中的应用
  • 细节解密NDIS协议驱动为什么能捕获到发送包
  • Geofence是什么
  • 10个确保微服务与容器安全的最佳实践
  • Git初体验
  • input实现文字超出省略号功能
  • interface和setter,getter
  • PHP那些事儿
  • php中curl和soap方式请求服务超时问题
  • Terraform入门 - 1. 安装Terraform
  • Vue.js 移动端适配之 vw 解决方案
  • XForms - 更强大的Form
  • 短视频宝贝=慢?阿里巴巴工程师这样秒开短视频
  • 悄悄地说一个bug
  • 使用agvtool更改app version/build
  • 树莓派 - 使用须知
  • 突破自己的技术思维
  • 最近的计划
  • 直播平台建设千万不要忘记流媒体服务器的存在 ...
  • ​ssh免密码登录设置及问题总结
  • #HarmonyOS:Web组件的使用
  • #WEB前端(HTML属性)
  • #预处理和函数的对比以及条件编译
  • (8)STL算法之替换
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (C++17) std算法之执行策略 execution
  • (八)Spring源码解析:Spring MVC
  • (二)linux使用docker容器运行mysql
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (剑指Offer)面试题34:丑数
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (一)Linux+Windows下安装ffmpeg
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • (转)nsfocus-绿盟科技笔试题目
  • (转)大型网站架构演变和知识体系
  • (转载)CentOS查看系统信息|CentOS查看命令
  • (状压dp)uva 10817 Headmaster's Headache
  • ******之网络***——物理***
  • .NET Core 2.1路线图
  • .NET delegate 委托 、 Event 事件
  • .NET Framework与.NET Framework SDK有什么不同?
  • .NET 读取 JSON格式的数据