转载请保留完整信息!

作者:中山大学 赵耀 10389332

创作时间:2013.11.25

发表时间:2013.12.11

背景

  遍历算法一般可按深度优先或广度优先进行。对于二叉树,深度优先遍历可分为前序、中序、后序遍历,而广度优先遍历对应于层先遍历。

  二叉树的三种深度优先遍历的递归算法和层先遍历都十分简洁。可是二叉树的三种非递归深度优先遍历算法却抽象而难于理解,并且各自区别,形式上并不统一。

  本文从另一种视角观察二叉树遍历的实质,给出一种基于计数栈的非递归二叉树遍历算法。该算法易于理解和记忆,并且将三种深搜遍历相统一,形式优雅。

另一种视角观察二叉树遍历的实质

  如图,对任一二叉树,按箭头方向(左手扶墙的方式)可以完全经过所有节点,并且每个节点经过三次。该方式可描述为:每当到达一个节点,如果是第一次到达该节点,则向左拐,如果是第二次到达,则向右拐,如果是第三次到达,则向上拐。若所拐方向没有节点,也算作一次拐出和一次拐入。

  在这种方式下,如果在每次到达一个节点时插入对该节点的访问函数,则形成了对应的前序、中序、后序遍历,其对应关系如下:

第一次到达-前序

第二次到达-中序

第三次到达-后序

二叉树中路径与栈的对应

   如图,二叉树中,从根节点到任一节点所形成的路径中,到终点节点所在层为止,每一层有且仅有一个节点存在于这条路径中。因此,这条路径序列可以和一个栈序列相对应,其中,栈的第n个元素代表路径序列中,位于二叉树第n层的元素。

算法思想

  根据以上分析,我们只需要模拟按箭头经过每一节点的过程,并在每次进入插入对应的访问函数,就可以形成对应的遍历算法。

   如何模拟呢?当每次进入一个节点,我们需要根据已经进入该节点的次数来决定下一节和所使用的访问函数,所以首先需要对节点的进入次数进行计数。计数和访问该节点之后,下一节点可能是子节点,也可能是父节点。综上,可以通过对每一节点的数据结构增设一计数量和一父指针来实现该算法。

  现在讨论另一种实现方式,即通过计数栈来实现。

   基于以上分析,由于每一条路径唯一对应于一个栈状态,并且,如果栈的元素是节点,那么栈中每一元素的上一个元素就是该节点的父节点,所以可以一边访问每一个节点,一边形成对应的栈,在需要返回父节点时,只需要出栈上一节点即可,并且,注意到任一节点只有在访问满3次后才会从栈中移除,因此可以将该计数值设置在栈元素上,而不必设置到每一个节点上。由于栈的最大深度对应于树的深度,所以相对于前一种实现方案,该方案将节省许多空间。

数据结构

struct BTree
{int value;BTree* lchild;BTree* rchild;
};

具体算法

void travel(BTree* tree)
{stack<pair<BTree*, int>> work_stack;work_stack.push(make_pair(tree, 0)); while (!work_stack.empty()){pair<BTree*, int> p = work_stack.top();work_stack.pop();// increase the count to indicate that the node// has been entered (again)int count = ++p.second;if (count == 1){preVisit(p.first);work_stack.push(p);if (p.first->lchild != NULL)work_stack.push(make_pair(p.first->lchild, 0));}else if (count == 2){inVisit(p.first);work_stack.push(p);if (p.first->rchild != NULL)work_stack.push(make_pair(p.first->rchild, 0));}// if it's the third time entering the node// then don't push it because we will never// come back againelsepostVisit(p.first);}
}

演示代码

void preVisit(BTree* node)
{cout << "first time visit node " << node->value << endl;
}void inVisit(BTree* node)
{cout << "second time visit node " << node->value << endl;
}void postVisit(BTree* node)
{cout << "third time visit node " << node->value << endl;
}const int SIZE = 7;int main()
{BTree nodes[SIZE];for (int i = 0; i < SIZE; i++){nodes[i].value = i+1;int l = i*2+1;int r = i*2+2;if (l < SIZE)nodes[i].lchild = &nodes[l];elsenodes[i].lchild = NULL;if (r < SIZE)nodes[i].rchild = &nodes[r];elsenodes[i].rchild = NULL;}travel(nodes);
}

演示结果

基于计数栈的非递归二叉树遍历算法相关推荐

  1. 九十五、二叉树的递归和非递归的遍历算法模板

    @Author:Runsen 刷Leetcode,需要知道一定的算法模板,本次先总结下二叉树的递归和非递归的遍历算法模板. 二叉树的四种遍历方式,前中后加上层序遍历.对于二叉树的前中后层序遍历,每种遍 ...

  2. 查找树的指定层级_非递归层次遍历方法实现二叉树中指定节点的层次数查找

    数据结构教材中,提供了基于队列实现一个二叉树的非递归层次遍历算法.但对于一个任意二叉树,如果要查找其中任何一个节点所在的层次数,教科书中并没有给出基于层次遍历的非递归算法.鉴于层次遍历算法比较容易理解 ...

  3. 二叉树遍历算法详解(递归法+非递归法)

    二叉树遍历算法详解 在上一篇C语言实现二叉树中有提到对于二叉树的遍历,包括前序,中序和后续遍历,以及层次遍历 大家已经熟悉了二叉树的前中后序遍历过程,大部分都采用了递归的思想来实现 在leetcode ...

  4. 二叉树的几种递归和非递归式遍历:

    二叉树的几种递归和非递归式遍历: 1 #include <fstream> 2 #include <iostream> 3 4 using namespace std; 5 6 ...

  5. 二叉树遍历算法的六种c语言实现 递归与非递归

    二叉树遍历分为三种: 先序遍历:先访问根结点,其次左节点,最后右节点 中序遍历:先访问左结点,其次跟节点,最后右节点 后序遍历:先访问左结点,其次右节点,最后根节点 三种遍历的递归算法实现形式类似,仅 ...

  6. 中序建立二叉树,非递归前序遍历二叉树

    内容: 编写程序,实现下述功能,并上机调试通过. 按中序顺序建立一棵二叉树: 用非递归方式遍历二叉树(先序),输出遍历序列. 步骤: 算法分析 采用二叉链表做存储结构,建立二叉树,借助于栈结构来实现二 ...

  7. 非递归前序遍历二叉树,非递归中序遍历二叉树,非递归后续遍历二叉树

    import java.util.Stack;public class Front {//非递归前序遍历public void front(TreeNode node) {Stack<TreeN ...

  8. 二叉树遍历算法的应用——计算二叉树的深度、计算二叉树的节点总数、计算二叉树的叶子节点数(均采用递归的思想)

    //二叉树遍历算法的应用 //1.计算二叉树的深度 int Depth(BitTree T){if(T==NULL)return 0;//如果是空树,返回0else{m=Depth(T->lch ...

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

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

最新文章

  1. 2021年大数据Spark(四):三种常见的运行模式
  2. 万人马拉松赛事,人脸识别系统如何快速、准确完成校验?
  3. 图片上传时即时生成多个缩略图
  4. 【免费活动】解析腾讯云音视频通信三大核心网络技术实战与创新
  5. 云服务远程登录---设置安全组
  6. 打车应用上马快递业务靠谱吗?
  7. 「leetcode」46.全排列【回溯算法】详细图解!
  8. 谷粒商城:13.分布式基础篇总结
  9. Rust: ,clone,Box,Arc 的address
  10. c语言11章谭浩强,谭浩强 C语言 第11章 结构体.ppt
  11. [BUUCTF]REVERSE——相册
  12. matlab已知斜率画直线,MATLAB 霍夫变换 连接斜率相同的直线
  13. 网狐6603服务器文档,【整理发布】网狐 6603 棋牌平台搭建图文详解(二)
  14. Nginx反向代理处理跨域问题
  15. linux调整逻辑卷大小,调整Linux逻辑卷大小
  16. SQL(oracle)常用命令
  17. 重要的是商业,不是应用(Building a bussiness, not an app)
  18. 使用jmater执行post请求,传参为json时报错
  19. 对付木马:空手入白刃谁动了我的电脑系统(转)
  20. 灵魂发问!Java并发和线程池,只言片语真的可以讲清楚吗?

热门文章

  1. 复利计算器结对2.0
  2. html5中新增标签的兼容性如何设置,HTML5新标签的兼容性处理
  3. 由于和IBM合作“IBM软件人才联盟”的项目,在社区开一个“IBM人才论坛”
  4. 2018 Intellij IDEA 最新旗舰版注册激活破解
  5. SAP 智能机器人流程自动化(iRPA)解决方案分享
  6. 大明宫发动机和个人的考虑发达国家
  7. 1.#INF、-1.#INF、1.#IND、-1.#IND 问题
  8. 【权限提升】61 Redis Postgresql数据库提权
  9. python三个数从小到大排序
  10. exe4j详细使用教程(附下载安装链接)