二叉树先序遍历的非递归算法具体实现


Posted in Javascript onJanuary 09, 2014

在前面一文,说过二叉树的递归遍历算法(二叉树先根(先序)遍历的改进),此文主要讲二叉树的非递归算法,采用栈结构

总结先根遍历得到的非递归算法思想如下:

1)入栈,主要是先头结点入栈,然后visit此结点

2)while,循环遍历当前结点,直至左孩子没有结点

3)if结点的右孩子为真,转入1)继续遍历,否则退出当前结点转入父母结点遍历转入1)

先看符合此思想的算法:

int PreOrderTraverseNonRecursiveEx(const BiTree &T, int (*VisitNode)(TElemType data))
{
 if (T == NULL)
 {
  return -1;
 }
 BiTNode *pBiNode = T;
 SqStack S;
 InitStack(&S);
 Push(&S, (SElemType)T);
 while (!IsStackEmpty(S))
 {
  while (pBiNode)
  {
   VisitNode(pBiNode->data);
   if (pBiNode != T)
   {
    Push(&S, (SElemType)pBiNode);
   }   
   pBiNode = pBiNode->lchild;
  }
  if(pBiNode == NULL)
  {
   Pop(&S, (SElemType*)&pBiNode); 
  }  
  if ( pBiNode->rchild == NULL)
  {
   Pop(&S, (SElemType*)&pBiNode); //如果此时栈已空,就有问题
  }
  pBiNode = pBiNode->rchild;
 }
 return 0;
}

注意:1)这里使用了栈结构,可参看上文顺序结构存储的栈

            2)这里在保存结点的时候,我保存的是指针也就是结点的地址,将其变为int型存储,在pop的时候里面使用的是指针,所以取的是&pBiNode,而不是pBiNode,为什么请自行思考指针的使用,最好理解的就是BiTNode *pBiNode;定义改为BiTree pBiNode就很好理解了。

上面这个算法其实是错误的!为什么呢? 这里我检查好久,期间出现还出现过无限循环,也出现过从左子树退出后右边子树不显示,最后我修改了第一个while判断条件,为什么呢?因为如果在pop之后,栈已空但是右子树还有,就无法继续了,这个在我写出后并没有进行太多验证,后面再阐述,这里并没有压入null指针,看一下压入空指针的例子,主要是左子树为空的时候才压入栈的,如下:

int PreOrderTraverseNonRecursive(const BiTree &T, int (*VisitNode)(TElemType data))
{
 if (T == NULL)
 {
  return -1;
 }
 BiTNode *pBiNode = T;
 SqStack S;
 InitStack(&S);
 Push(&S, (SElemType)T);
 while (!IsStackEmpty(S))
 {
  GetTop(S, (SElemType*)&pBiNode);
  while (pBiNode)
  {
   VisitNode(pBiNode->data);  
   pBiNode = pBiNode->lchild;
   Push(&S, (SElemType)pBiNode);
  }
  if(pBiNode == NULL)
  { 
   Pop(&S, (SElemType*)&pBiNode);
  }  
  if ( !IsStackEmpty(S))
  {
   Pop(&S, (SElemType*)&pBiNode);
   pBiNode = pBiNode->rchild;
   Push(&S, (SElemType)pBiNode);
  }
 }
 return 0;
}

这里是这样的,先压入根节点,然后判断左子树是否为空,不为空就压入栈,否则退出while循环之后就将NULL结点出栈,再判断当前栈是否为空,如果非空就出栈得到父节点然后判断右孩子,压入右孩子结点,再判断此右子树的左孩子是否为空,继续循环。

这里有两个浪费的地方:一个就是压入空孩子结点入栈,二就是频繁使用GetTop获得栈顶元素

这里返回过来再看初开始设计的算法,那里正好没有压入NULL指针或者说空的孩子结点,但是并不能输出完整,这里我们想到可以在判断栈的时候加入,当前的结点是否为NULL就可以了,这样就不会出现不会显示退出左子树结点不能显示右子树结点的尴尬了,如下:

//非递归先序遍历二叉树
int PreOrderTraverseNonRecursiveEx(const BiTree &T, 
           int (*VisitNode)(TElemType data))
{
 if (T == NULL)
 {
  return -1;
 }
 BiTNode *pBiNode = T;
 SqStack S;
 InitStack(&S);
 Push(&S, (SElemType)T);
 while ( !IsStackEmpty(S) || pBiNode)  //主要修改的就是这句
 {
  while (pBiNode)
  {
   VisitNode(pBiNode->data);
   if (pBiNode != T)
   {
    Push(&S, (SElemType)pBiNode);
   }   
   pBiNode = pBiNode->lchild;
  }
  if(pBiNode == NULL)
  {
   Pop(&S, (SElemType*)&pBiNode); 
  }  
  if ( pBiNode->rchild == NULL)
  {
   Pop(&S, (SElemType*)&pBiNode); //如果此时栈已空,就有问题
  }
  pBiNode = pBiNode->rchild;
 }
 return 0;
}

在第一个while循环加入这个之后,就可以了,测试用例与二叉树先序遍历类似。如下测试上节的二叉树例子:

二叉树先序遍历的非递归算法具体实现

此时输入的数据仍然还是 12 34 0 0 78 0 0,测试结果如下:

--- BiTree ---
Please Enter BiTree Node data:
12
Please Enter BiTree Node data:
34
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
78
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
0
12 34 78

这个还不足以测试,再看如下的二叉树

二叉树先序遍历的非递归算法具体实现

此时输入数据应该为:12 34 24 0 0 50 0 0 78 37 0 0 0,测试结果如下:

--- BiTree ---
Please Enter BiTree Node data:
12
Please Enter BiTree Node data:
34
Please Enter BiTree Node data:
24
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
50
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
78
Please Enter BiTree Node data:
37
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
0
12 34 24 50 78 37

由先序遍历可知,正好是正确的,另外这些算法不光是对先序遍历的,如果想变为中序或者后序,只需将上面算法中的visit之类的先去掉,然后将它加入合适的位置,就可以了

Javascript 相关文章推荐
一个加密JavaScript的开源工具PACKER2.0.2
Nov 04 Javascript
jQuery侧边栏随窗口滚动实现方法
Mar 04 Javascript
DOM节点的替换或修改函数replaceChild()用法实例
Jan 12 Javascript
JavaScript+html5 canvas制作色彩斑斓的正方形效果
Jan 27 Javascript
基于Bootstrap的后台管理面板 Bootstrap Metro Dashboard
Jun 17 Javascript
json对象转为字符串,当做参数传递时加密解密的实现方法
Jun 29 Javascript
详解Vue CLI3 多页应用实践和源码设计
Aug 30 Javascript
解决layui checkbox 提交多个值的问题
Sep 02 Javascript
javascript异常处理实现原理详解
Feb 17 Javascript
Javascript原型链及instanceof原理详解
May 25 Javascript
在vue中axios设置timeout超时的操作
Sep 04 Javascript
js获取url页面id,也就是最后的数字文件名
Sep 25 Javascript
IE下Ajax缓存问题的快速解决方法(get方式)
Jan 09 #Javascript
js/jquery解析json和数组格式的方法详解
Jan 09 #Javascript
JS获取节点的兄弟,父级,子级元素的方法
Jan 09 #Javascript
js与jquery获取父级元素,子级元素,兄弟元素的实现方法
Jan 09 #Javascript
js与jquery获取父元素,删除子元素的两种不同方法
Jan 09 #Javascript
浅析jQuery(function(){})与(function(){})(jQuery)之间的区别
Jan 09 #Javascript
fmt:formatDate的输出格式详解
Jan 09 #Javascript
You might like
php中flush()、ob_flush()、ob_end_flush()的区别介绍
2013/02/17 PHP
PHP 面向对象程序设计(oop)学习笔记(一) - 抽象类、对象接口、instanceof 和契约式编程
2014/06/12 PHP
php实现RSA加密类实例
2015/03/26 PHP
PHP常用的排序和查找算法
2015/08/06 PHP
如何通过View::first使用Laravel Blade的动态模板详解
2017/09/21 PHP
jQuery学习笔记之控制页面实现代码
2012/02/27 Javascript
js鼠标滑过弹出层的定位IE6bug解决办法
2012/12/26 Javascript
基于jQuery.Validate验证库知识点的详解
2013/04/26 Javascript
JQuery 图片滚动轮播示例代码
2014/03/24 Javascript
JS获取当前日期时间并定时刷新示例
2021/03/04 Javascript
React Native时间转换格式工具类分享
2017/10/24 Javascript
详解如何在vue-cli中使用vuex
2018/08/07 Javascript
vue中子组件的methods中获取到props中的值方法
2018/08/27 Javascript
详解微信小程序框架wepy踩坑记录(与vue对比)
2019/03/12 Javascript
vue实现轮播图帧率播放
2021/01/26 Vue.js
教你安装python Django(图文)
2013/11/04 Python
Python使用logging结合decorator模式实现优化日志输出的方法
2016/04/16 Python
Python编程之变量赋值操作实例分析
2017/07/24 Python
浅谈python配置与使用OpenCV踩的一些坑
2018/04/02 Python
python2与python3共存问题的解决方法
2018/09/18 Python
opencv-python 提取sift特征并匹配的实例
2019/12/09 Python
python网络编程之五子棋游戏
2020/05/14 Python
利用keras使用神经网络预测销量操作
2020/07/07 Python
pycharm全局搜索的具体步骤
2020/07/28 Python
css3的动画特效之动画序列(animation)
2017/12/22 HTML / CSS
很酷的小工具和电子产品商城:GearBest
2016/11/19 全球购物
马来西亚航空官方网站:Malaysia Airlines
2017/07/28 全球购物
加利福尼亚州威尼斯的女性奢侈品设计师服装和概念店:Mona Moore
2018/09/13 全球购物
Turnbull & Asser官网:英国皇室御用的顶级定制衬衫
2019/01/31 全球购物
一套Delphi的笔试题一
2016/02/14 面试题
十佳好少年事迹材料
2014/08/21 职场文书
《女娲补天》教学反思
2016/02/20 职场文书
创业计划书之烤红薯
2019/09/26 职场文书
CSS3实现的水平标题菜单
2021/04/14 HTML / CSS
高考要来啦!用Python爬取历年高考数据并分析
2021/06/03 Python
MySQL 聚合函数排序
2021/07/16 MySQL