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

我们知道,要想处理二叉树,离不开其遍历方式。遍历二叉树有三种方式,前序遍历,中序遍历,后序遍历,每种方式都有递归版本和非递归版本。

  • 前序遍历——先遍历根节点,再遍历左右节点;
  • 中序遍历——先遍历左节点,再遍历根节点,最后遍历右节点;
  • 后序遍历——先遍历左右节点,再遍历根节点。

所以,其实遍历规则很简单,永远都是先遍历左子节点,再遍历右子节点,而什么时候遍历根节点由遍历方式决定。

递归遍历

下面先看看简单的递归遍历二叉树代码,优点是代码简单易读易懂,且容易编写,缺点是容易导致栈溢出。
二叉树节点定义:

class TreeNode {
public:TreeNode(int val_) :val(val_), left(NULL), right(NULL) {}TreeNode* left;TreeNode* right;int val;
};

前序遍历——递归:

void PreOrder(TreeNode* root) {if (root != NULL) {cout << root->val << endl;PreOrder(root->left);PreOrder(root->right);}
}

中序遍历——递归:

void InOrder(TreeNode* root) {if (root != NULL) {InOrder(root->left);cout << root->val << endl;InOrder(root->right);}
}

后序遍历——递归:

void PostOrder(TreeNode* root) {if (root != NULL) {PostOrder(root->left);PostOrder(root->right);cout << root->val << endl;}
}

可以发现,递归遍历十分简单,不过需要注意的是不要忘了边界条件。

非递归遍历

非递归遍历的主要思想是通过循环与栈这种数据结构来解决遍历二叉树的问题。
前序遍历——非递归:
先访问根节点,再分别访问左右子节点;非递归前序遍历用栈来模拟遍历过程,
1. 首先将根节点入栈
2. 开始循环,访问根节点,再将根节点出栈
3. 根节点左子节点入栈,最后右子节点入栈,完成一次循环
4. 接下来循环进行先前3个步骤的处理直到栈为空则完成遍历。

void PreOrderNonRecursive(TreeNode* root) {stack<TreeNode*> sta;if (root == NULL)return;sta.push(root);while (!sta.empty()) {TreeNode* pNode = sta.top();cout << pNode->val << endl;sta.pop();if (pNode->right != NULL)sta.push(pNode->right);if (pNode->left != NULL)sta.push(pNode->left);}
}

中序遍历——非递归:
中序遍历稍微复杂一点,先访问左子节点,再访问根节点,最后访问右子节点;同样需要借助栈来进行遍历,
1. 由于最先访问左子节点,所以需要先访问最左边的节点,先将根节点入栈,用一个中间变量记录每次入栈节点,以便判断其是否有左子节点
2. 如果中间入栈的节点有左子节点,那么继续入栈其左子节点,直到不再有左子节点为止
3. 栈顶元素出栈,即访问根节点
4. 判断栈顶元素是否有右子节点,若有,入栈其右子节点
5. 循环2-4步骤,直到栈为空,完成遍历。

void InOrderNonRecursive(TreeNode* root) {stack<TreeNode*> sta;if (root == NULL)return;sta.push(root);TreeNode* pNode = root;while (!sta.empty()) {while (pNode != NULL&&pNode->left != NULL) {pNode = pNode->left;sta.push(pNode);}pNode = sta.top();cout << pNode->val << endl;sta.pop();if (pNode->right != NULL) {sta.push(pNode->right);pNode = pNode->right;}elsepNode = NULL;}
}

后序遍历——非递归:
先遍历左右子节点,最后遍历根节点,非递归后序遍历是三种遍历方式中最复杂的一种,不过只要抓住一点基本思想就行:判断每个节点的左右子节点是否为NULL,或者其左右子节点是否已经被访问过,如果是,那么访问该节点。
1. 边界条件检查,入栈根节点
2. 记录当前栈顶元素,如果栈顶元素左右子节点均为NULL或者均已被访问,那么访问当前节点
3. 否则依次入栈其右子节点和左子节点
4. 循环步骤2和步骤3,直到栈为空结束

void PostOrderNonRecursive(TreeNode* root) {if (root == NULL)return;stack<TreeNode*> sta;TreeNode* curNode = root;TreeNode* preNode = NULL;sta.push(root);while (!sta.empty()) {curNode = sta.top();if ((curNode->left == NULL&&curNode->right == NULL) || (preNode != NULL && (preNode == curNode->left || preNode == curNode->right))) {cout << curNode->val << endl;sta.pop();preNode = curNode;}else {if (curNode->right != NULL)sta.push(curNode->right);if (curNode->left != NULL)sta.push(curNode->left);}}
}

测试代码

上边分别介绍了三种遍历二叉树的方法,递归与非递归版本。下边对代码做一个简单的测试,分别以前中后序遍历输出二叉树的值,感兴趣的读者可以自行修改测试方法。
另外在构建测试用的二叉树时,正好用上不久前的算法:用前序遍历和后序遍历的二叉树序列,重构二叉树。下边贴上测试代码:

class Solution {
public:TreeNode* reConstructBinaryTree(vector<int> pre, vector<int> vin) {if (pre.size() == 0 || vin.size() == 0)return NULL;return construct(pre, vin, 0, pre.size(), 0, vin.size());}TreeNode* construct(vector<int>& pre, vector<int>& vin, int startPreorder, int endPreorder, int startInorder, int endInorder) {int rootValue = pre[startPreorder];TreeNode* root = new TreeNode(rootValue);if (startPreorder == endPreorder) {if (startInorder == endInorder - 1 && pre[startPreorder] == vin[startInorder])return root;}int rootInorder = 0;for (int i = startInorder; i < endInorder; i++) {if (vin[i] != rootValue)continue;else {rootInorder = i;break;}}if (rootInorder - startInorder > 0) {root->left = construct(pre, vin, startPreorder + 1, startPreorder + rootInorder - startInorder + 1, startInorder, rootInorder);}if (rootInorder - startInorder < endPreorder - startPreorder - 1) {root->right = construct(pre, vin, startPreorder + rootInorder - startInorder + 1, endPreorder, rootInorder + 1, endInorder);}return root;}
};int main() {vector<int> pre = { 1,2,4,7,8,9,5,3,6 };vector<int> vin = { 4,8,7,9,2,5,1,6,3 };Solution sol;TreeNode* root = sol.reConstructBinaryTree(pre, vin);           //构建二叉树PreOrder(root);                                                 //递归前序遍历InOrder(root);                                                  //递归中序遍历PostOrder(root);                                                //递归后序遍历 PreOrderNonRecursive(root);                                     //非递归前序遍历InOrderNonRecursive(root);                                      //非递归中序遍历PostOrderNonRecursive(root);                                    //非递归后序遍历return 0;
}

代码写得比较仓促,如有错误还请大家指出。谢谢。

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

  1. 二叉树的前序中序后序三种遍历方式及递归算法介绍

    二叉树三种遍历方式 二叉树的遍历是整个二叉树的核心,二叉树的几本操作都要依赖于遍历,对于二叉树的遍历,递归是最简单也最容易理解的,本文详细介绍了二叉树的三种遍历方法,并用递归来实现: 完整的可调试代码 ...

  2. java 建树源码_Java实现的二叉树常用操作【前序建树,前中后递归非递归遍历及层序遍历】...

    import java.util.ArrayDeque; import java.util.Queue; import java.util.Stack; //二叉树的建树,前中后 递归非递归遍历 层序 ...

  3. java建树_Java实现的二叉树常用操作【前序建树,前中后递归非递归遍历及层序遍历】...

    本文实例讲述了Java实现的二叉树常用操作.分享给大家供大家参考,具体如下: import java.util.ArrayDeque; import java.util.Queue; import j ...

  4. 【二叉树Java】二叉树遍历前序中序后序遍历的非递归写法

    本文主要介绍二叉树前序中序后序遍历的非递归写法 在探讨如何写出二叉树的前序中序后序遍历代码之前,我们先来明确一个问题,前序中序后序遍历根据什么区分? 二叉树的前序中序后序遍历,是相较根节点说的.最先遍 ...

  5. 二叉树的非递归遍历(前序中序后序非递归C语言)

    前两天做数据结构实验,要求用非递归算法遍历二叉树.只知道用栈来储存数据,具体算法还不太清楚.经过两天的搜索,看到网上很多种解法,很多解法都是用C++来写的算法,一直找不到用C语言写的算法,所以就总结了 ...

  6. 二叉树的四种遍历方式——前序、中序、后序、层序遍历(递归+非递归实现)

    如果N代表根节点,L代表根节点的左子树,R代表根节点的右子树,则根据遍历根节点的先后次序有以下遍历方式: 1. NLR:前序遍历(Preorder Traversal 亦称先序遍历)--访问根结点-- ...

  7. 一文彻底搞定二叉树的前序、中序、后序遍历(图解递归非递归)

    前言 大家好,我是bigsai,在数据结构与算法中,二叉树无论是考研.笔试都是非常高频的考点内容,在二叉树中,二叉树的遍历又是非常重要的知识点,今天给大家讲讲二叉树的层序遍历. 这部分很多人可能会但是 ...

  8. 二叉树的遍历:先序 中序 后序遍历的递归与非递归实现及层序遍历

    二叉树的定义:一种基本的数据结构,是一种每个节点的儿子数目都不多于2的树 树节点的定义如下: // 树(节点)定义 struct TreeNode {int data; // 值TreeNode* l ...

  9. java中二叉树_Java工程师面试1000题224-递归非递归实现二叉树前、中、后序遍历...

    224.使用递归和非递归实现二叉树的前.中.后序遍历 使用递归来实现二叉树的前.中.后序遍历比较简单,直接给出代码,我们重点讨论非递归的实现. class Node { public int valu ...

  10. 二叉树的深度(前序 中序 后序 递归非递归搜素)、广度、搜索 C++

    a b c 使用 1 2 3 表示 /* 描述:二叉树的深度(前序 中序 后序 递归非递归搜素).广度.搜索 作者:jz 日期:20140819 */ #include<stdio.h> ...

最新文章

  1. Windows 任务管理器进程详解
  2. Hadoop详解(九):Hadoop Streaming和Pipes原理和实现
  3. 不吹牛逼,撸个注解有什么难的
  4. Python常见文件函数
  5. 那位 13 岁就当上老板的开发者是如何炼成的?
  6. 磁共振成像(MRI)影像心脏组织分割
  7. 矩阵的伴随矩阵的伴随矩阵
  8. oracle 11g 重置,oracle数据库重置
  9. python有几级等级考试成绩查询_python查询46级成绩
  10. idea任务栏图标变白色,桌面快捷方式变白色
  11. 杭州云栖大会“弹性计算用户实践专场”等你来
  12. Latex 插入的图片紧跟当前文字
  13. 什么是序列化与反序列化,为什么序列化
  14. springboot实战,实现发送邮件,gmail邮件,包括发送附件还有正文,以及注意覆盖问题
  15. 对复利计算器和股票投资的总结
  16. FPGA与普通CPU的区别
  17. 在移动硬盘上安装Ubuntu
  18. es module 和 commonjs 模块化实践
  19. 核心微生物分析_基因测序找出肠道核心微生物群
  20. 华为Java编码规范

热门文章

  1. “地理信息+物联网”创新东坝乡基层治理新模式
  2. Labview程序内存释放
  3. 企业内容管理(ECM)市场 谁执牛耳?
  4. 从德国勒索软件活动看恶意代码的生存方式-云栖社区-阿里云
  5. NDK开发前奏 - x团参数加密和签名校验
  6. (2分钟解决)Windows找不到文件报错:‘C:\Program Files(x86)\Adobe\Acrobat DC\Acrobat\AcroTray.exe’。请确定文件名是否正确后,再试一次
  7. 分享我的电子藏书:Windows编程(共4本)
  8. 真正的美剧字幕组翻译高手教你如何学好英语!心得经验之谈啊!
  9. iOS开发之第三方分享微博分享、微博分享失败原因总结,史上最新最全第三方分享微博方式实现。 微博分享各种坑总结
  10. 蓝桥冲刺31天打卡—Day10