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


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 相关文章推荐
在IE下:float属性会影响offsetTop的取值
Dec 22 Javascript
javascript编程起步(第六课)
Feb 27 Javascript
JavaScript中的console.profile()函数详细介绍
Dec 29 Javascript
jQuery事件绑定on()、bind()与delegate() 方法详解
Jun 03 Javascript
js实现仿爱微网两级导航菜单效果代码
Aug 31 Javascript
xtemplate node.js 的使用方法实例解析
Aug 22 Javascript
jquery做个日期选择适用于手机端示例
Jan 10 Javascript
JavaScript中localStorage对象存储方式实例分析
Jan 12 Javascript
angularjs使用directive实现分页组件的示例
Feb 07 Javascript
ES6新特性之Symbol类型用法分析
Mar 31 Javascript
详解关于react-redux中的connect用法介绍及原理解析
Sep 11 Javascript
浅谈Node.js 子进程与应用场景
Jan 24 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 str_replace的替换漏洞
2008/03/15 PHP
使用PHP编写的SVN类
2013/07/18 PHP
YII2.0之Activeform表单组件用法实例
2016/01/09 PHP
使用PHPStorm+XDebug搭建单步调试环境
2017/11/19 PHP
让广告代码不再影响你的网页加载速度
2006/07/07 Javascript
Jquery判断IE6等浏览器的代码
2011/04/05 Javascript
jQuery使用数组编写图片无缝向左滚动
2012/12/11 Javascript
jQuery中attr()和prop()在修改checked属性时的区别
2014/07/18 Javascript
超链接的禁用属性Disabled使用示例
2014/07/31 Javascript
值得分享的Bootstrap Table使用教程
2016/11/23 Javascript
微信小程序 scroll-view实现上拉加载与下拉刷新的实例
2017/01/21 Javascript
ES6字符串模板,剩余参数,默认参数功能与用法示例
2017/04/06 Javascript
JavaScript实现的贝塞尔曲线算法简单示例
2018/01/30 Javascript
深入理解移动前端开发之viewport
2018/10/19 Javascript
angular中两种表单的区别(响应式和模板驱动表单)
2018/12/06 Javascript
vue axios重复点击取消上一次请求封装的方法
2019/06/19 Javascript
微信小程序实现一张或多张图片上传(云开发)
2019/09/25 Javascript
JS中this的4种绑定规则详解
2020/02/04 Javascript
Vue data的数据响应式到底是如何实现的
2020/02/11 Javascript
如何基于viewport vm适配移动端页面
2020/11/13 Javascript
Python版的文曲星猜数字游戏代码
2013/09/02 Python
浅谈django中的认证与登录
2016/10/31 Python
Python带动态参数功能的sqlite工具类
2018/05/26 Python
python tkinter GUI绘制,以及点击更新显示图片代码
2020/03/14 Python
python读取mysql数据绘制条形图
2020/03/25 Python
德国咖啡批发商:Coffeefair
2019/08/26 全球购物
广告设计应届生求职信
2014/03/01 职场文书
法人代表委托书
2014/04/04 职场文书
查摆问题整改措施范文
2014/10/11 职场文书
先进班组事迹材料
2014/12/25 职场文书
千与千寻观后感
2015/06/04 职场文书
校园歌手大赛主持词
2015/07/03 职场文书
php 解析非标准json、非规范json
2021/04/01 PHP
Android Flutter实现3D动画效果示例详解
2022/04/07 Java/Android
Golang解析JSON对象
2022/04/30 Golang
WinServer2012搭建DNS服务器的方法步骤
2022/06/10 Servers