文章目录

  • 1.深度优先遍历
    • 1.1 先序遍历
    • 1.2 中序遍历
    • 1.3 后序遍历
  • 2. 广度优先遍历
  • 3.验证结果
  • 参考文献

二叉树的遍历分为两类,一类是深度优先遍历,一类是广度优先遍历。

1.深度优先遍历

二叉树的深度优先遍历有三种方式,先序(先根次序)、中序(中根次序)和后序(后根次序)遍历。

因为树的定义本身是递归定义,因此采用递归的方法去实现树的三种遍历不仅容易理解而且代码很简洁。若采用非递归的方法遍历二叉树,需要采用栈去模拟实现。在三种遍历中,前序和中序遍历的非递归算法都很容易实现,非递归后序遍历实现起来相对来说要难一点。下面一一讲解具体的递归和非递归实现。

1.1 先序遍历

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

  • 递归实现
//先根递归遍历
void preOrderRecursion(BinaryTreeNode* root)
{if(root==NULL) return;cout<< " " << root->m_key;preOrderRecursion(root->m_pLeft);preOrderRecursion(root->m_pRight);
}
  • 非递归实现

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

给定二叉树的根结点 R:
(1)并将根结点 R 入栈;
(2)判断栈是否为空,若不为空,取栈顶元素 cur 访问并出栈。然后先将 cur 的右子结点入栈,再将 cur 的左子结点入栈;
(3)重复(3)直到栈空,则遍历结束。

// 先序非递归遍历,需要使用栈
void preOrderStack(BinaryTreeNode* root)
{if(root==NULL) return;stack<BinaryTreeNode*> stack;stack.push(root);BinaryTreeNode* cur=NULL;while(!stack.empty()){cur=stack.top();cout<<" "<<cur->m_key; //visitstack.pop();if(cur->m_pRight!=NULL){stack.push(cur->m_pRight);}if(cur->m_pLeft!=NULL){stack.push(cur->m_pLeft);}}
}

1.2 中序遍历

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

  • 递归实现
// 中序递归遍历
void midOrderRecursion(BinaryTreeNode* root){if(root==NULL) return;midOrderRecursion(root->m_pLeft);cout<<" "<<root->m_key;   //visitmidOrderRecursion(root->m_pRight);
}
  • 非递归实现

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

对于给定的二叉树根结点 R,
(1)若其左孩子不为空,循环将 R 及 R 左子树中的所有结点的左孩子入栈;
(2)取栈顶元素 cur,访问 cur 并将 cur 出栈。然后对 cur 的右子结点进行步骤(1)那样的处理;
(3)重复(1)和(2)的操作,直到 cur 为空且栈为空。

// 中根非递归遍历,需要使用栈
void midOrderStack(BinaryTreeNode* root)
{if(root==NULL) return; stack<BinaryTreeNode*> stack;BinaryTreeNode* cur=root;while(!stack.empty() || cur!=NULL){while(cur){  stack.push(cur);  cur=cur->m_pLeft;  }  cur=stack.top();  cout<<" "<<cur->m_key;   //visitstack.pop();  cur=cur->m_pRight;  }
}

1.3 后序遍历

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

  • 递归实现
// 后根递归遍历
void postOrderRecursion(BinaryTreeNode* root)
{if(root==NULL) return;postOrderRecursion(root->m_pLeft);postOrderRecursion(root->m_pRight);cout << " " << root->m_key;
}
  • 非递归实现

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

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

// 非递归后序遍历,版本1
void postOrderStack1(BinaryTreeNode* root)
{if(root==NULL) return; stack<pair<BinaryTreeNode*, bool>> s;pair<BinaryTreeNode*,bool> cur=make_pair(root,true);while(cur.first!=NULL||!s.empty()){//沿左子树一直往下搜索,直至出现没有左子树的结点while(cur.first!=NULL){s.push(cur);cur=make_pair(cur.first->m_pLeft,true);}if(!s.empty()){//表示是第一次出现在栈顶if(s.top().second==true){ s.top().second=false;cur=make_pair(s.top().first->m_pRight,true); //将当前节点的右节点入栈}else{// 第二次出现在栈顶 cout << s.top().first->m_key << " ";s.pop();}}}
}

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

// 非递归后序遍历,版本 2
void postOrderStack2(BinaryTreeNode* root)
{   if(root==NULL) return;stack<BinaryTreeNode*> s;BinaryTreeNode* cur;     //当前结点 BinaryTreeNode* pre=NULL;   //前一次访问的结点s.push(root);while(!s.empty()){cur=s.top();//在判断当前结点时,左孩子和右孩子都在根结点前已经被访问if((cur->m_pLeft==NULL&&cur->m_pRight==NULL) || (pre!=NULL&&(pre==cur->m_pLeft || pre==cur->m_pRight))){cout<<cur->m_key<<" ";  //如果当前结点没有孩子结点或者孩子节点都已被访问过 s.pop();pre=cur; }else{if(cur->m_pRight!=NULL) s.push(cur->m_pRight);if(cur->m_pLeft!=NULL) s.push(cur->m_pLeft);}}
}

2. 广度优先遍历

广度优先周游的方式是按层次从上到下,从左到右的逐层访问,不难想到,可以利用一个队列来实现。基本思想如下:
(1)首先把二叉树的根节点送入队列;
(2)队首的节点出队列并访问之,然后把它的右子节点和左子节点分别入队列;
(3)重复上面两步操作,直至队空。

// 广度优先遍历二叉树,使用队列实现
void breadthFirstOrder(BinaryTreeNode* root)
{if(root==NULL) return;queue<BinaryTreeNode*> queue;queue.push(root);while(!queue.empty()){BinaryTreeNode* cur=queue.front();cout<<" "<<cur->m_key;//visitqueue.pop();if(cur->m_pLeft!=NULL) queue.push(cur->m_pLeft);if(cur->m_pRight!=NULL) queue.push(cur->m_pRight);}
}

3.验证结果

以上面介绍的各种遍历,验证代码如下:

#include  <iostream>
#include <stack>
#include <queue>
using namespace std;// 二叉树节点结构体
struct BinaryTreeNode
{int m_key;BinaryTreeNode* m_pLeft;BinaryTreeNode* m_pRight;
};/****************************************
func:根据前序序列和中序序列构建二叉树
para:preOrder:前序序列;midOrder:中序序列;len:节点数
****************************************/
BinaryTreeNode* construct(int* preOrder,int* midOrder,int len)
{if(preOrder==NULL||midOrder==NULL||len<=0) return NULL;//先序遍历的第一个值是根结点的键值int rootKey=preOrder[0];BinaryTreeNode* root=new BinaryTreeNode;root->m_key=rootKey;root->m_pLeft=root->m_pRight=NULL;//只有一个节点if(len==1 && *preOrder == *midOrder) return root;//在中序遍历中找到根节点的值int* rootMidOrder=midOrder;int leftLen=0; //左子树节点数while(*rootMidOrder!=rootKey&&rootMidOrder<=(midOrder+len-1)){++rootMidOrder;++leftLen;}//在中序序列未找到根结点,输入错误if(*rootMidOrder!=rootKey) return NULL;//构建左子树if(leftLen>0){root->m_pLeft=construct(preOrder+1,midOrder,leftLen);}//构建右子树if(len-leftLen-1>0){root->m_pRight=construct(preOrder+leftLen+1,rootMidOrder+1,len-leftLen-1);}return root;
}int main()
{// 先序序列int preOrder[8]={1,2,4,7,3,5,6,8};// 中序序列int midOrder[8]={4,7,2,1,5,3,8,6};// 建树BinaryTreeNode* root=construct(preOrder, midOrder, 8);cout<<"---preOrder---"<<endl;cout<<"recursion version: ";preOrderRecursion(root);cout<<endl<<"stack version: ";preOrderStack(root);cout<<endl<<endl<<"---midOrder---"<<endl;cout<<"recursion version: ";midOrderRecursion(root);cout<<endl<<"stack version1: ";postOrderStack1(root);cout<<endl<<"stack version2: ";postOrderStack2(root);cout<<endl<<endl<<"---postOrder---"<<endl;cout<<"recursion version: ";postOrderRecursion(root);cout<<endl<<"stack version: ";postOrderStack1(root);cout<<endl<<endl<<"---Breadth First Order---"<<endl;breadthFirstOrder(root);
}

实验结果如下:

---preOrder---
recursion version:  1 2 4 7 3 5 6 8
stack version:  1 2 4 7 3 5 6 8---midOrder---
recursion version:  4 7 2 1 5 3 8 6
stack version:  4 7 2 1 5 3 8 6---postOrder---
recursion version:  7 4 2 5 8 6 3 1
stack version1: 7 4 2 5 8 6 3 1
stack version2: 7 4 2 5 8 6 3 1 ---Breadth First Order---1 2 3 4 5 6 7 8

参考文献

[1] CSDN.二叉树的非递归遍历
[2] CSDN.二叉树简介与构建

二叉树遍历(深度优先+广度优先)相关推荐

  1. 二叉树深度优先遍历和广度优先遍历

    二叉树深度优先遍历和广度优先遍历

  2. 二叉树的深度优先遍历和广度优先遍历

    二叉树是一种很重要的数据结构,对于二叉树的遍历,有深度优先遍历和广度优先遍历,深度优先遍历又有先序.中序.后续遍历,广度优先遍历就是按层遍历. 1. 深度优先遍历 深度优先遍历,也就是先序.中序.后续 ...

  3. PHP实现二叉树的深度优先遍历(前序、中序、后序)和广度优先遍历(层次) 转载陈小龙哈2017...

    http://blog.csdn.net/baidu_30000217/article/details/52953127 前言: 深度优先遍历:对每一个可能的分支路径深入到不能再深入为止,而且每个结点 ...

  4. 二叉树的深度优先和广度优先遍历

    图的深度优先搜索法是树的先根遍历的推广,它的基本思想是:从图G的某个顶点v0出发,访问v0,然后选择一个与v0相邻且没被访问过的顶点vi访问,再从vi出发选择一个与vi相邻且未被访问的顶点vj进行访问 ...

  5. 二叉树的深度优先遍历(DFS)与广度优先遍历(BFS)

    二叉树的深度优先遍历(DFS)与广度优先遍历(BFS) 深度优先遍历:从根节点出发,沿着左子树方向进行纵向遍历,直到找到叶子节点为止.然后回溯到前一个节点,进行右子树节点的遍历,直到遍历完所有可达节点 ...

  6. java实现二叉树广度优先遍历_二叉树之深度优先和广度优先遍历(Java)

    tree.png 1. 二叉树结构定义 public static class Tree { int data; Tree left; Tree right; public Tree(int data ...

  7. php 实现二叉树的最大深度_PHP实现二叉树的深度优先遍历(前序、中序、后序)和广度优先遍历(层次)...

    前言: 深度优先遍历:对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次.要特别注意的是,二叉树的深度优先遍历比较特殊,可以细分为先序遍历.中序遍历.后序遍历.具体说明如下: 前序遍 ...

  8. 树的基本概念和遍历规则 数据结构和算法 二叉树遍历(前序、中序、后序、层次、深度优先、广度优先遍历)

    zsychanpin 博客园 首页 新随笔 联系 订阅 管理 树的基本概念和遍历规则 树的递归定义 树是n(n>0)个结点的有限集,这个集合满足下面条件:       ⑴有且仅有一个结点没有前驱 ...

  9. 【树】二叉树遍历算法(深度优先、广度优先遍历,前序、中序、后序、层次)及Java实现...

    [树]二叉树遍历算法(深度优先.广度优先遍历,前序.中序.后序.层次)及Java实现 目录 一.前序遍历 二.中序遍历 三.后序遍历 四.层次遍历 遍历的作用 二叉树是一种非常重要的数据结构,很多其它 ...

最新文章

  1. GNU make manual 翻译(五十一)
  2. power系列服务器问题品管主任,了解 Power BI 管理员角色
  3. P1582 倒水(二进制)
  4. LIS (nlogn)的算法
  5. 电脑常见问题_解决PC常见问题 篇二十:垃圾佬手把手教你如何正确缩电脑配置砍预算...
  6. 21天Jmeter打卡合集之从入门到精通
  7. 基于python的音频播放器_基于python实现音乐播放器代码实例
  8. MongoDBTool - 测试版【GUI美化完毕】 源代码发布 --MongoDB爱好者,Winform爱好者 请进...
  9. html页面打开前判断session,js判断session过期
  10. 计算机英语截短词,英语词汇构词法(Word Formation)——截短法
  11. 英语口语收集(十三)
  12. Mysql的原子性、持久性原理
  13. Python学习之CSDN21天学习挑战赛计划之2
  14. Linux命令行与shell脚本(17)--正则表达式
  15. Embedded Linux S3C2440 - QEMU and Graphic
  16. 行云创新:车云一体化平台,实现软件定义汽车
  17. 苹果6手机服务器停止响应,iphone6被停用怎么办?苹果6被停用解决方法汇总
  18. 失焦的“她营销”,品牌营销困于女性议题
  19. 什么是wind量化平台接口?
  20. ue/um-editor实现word图片复制

热门文章

  1. 开源审计的最佳时机是什么时候?
  2. 签约!睿铂与泰瑞数创共同助力实景三维中国建设
  3. Sersync实时备份服务部署实践
  4. #55 #56 #58 #59 #60 #66
  5. 细数被程序员吐糟的9大困难(转)
  6. JFinal 调用 oracle 存储过程的 步骤
  7. Python之isinstance
  8. socket与TCP/UDP编程-转
  9. 蓝桥杯 ALGO-28 算法训练 星际交流
  10. [Python] L1-007. 念数字-PAT团体程序设计天梯赛GPLT