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


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读取RSS数据
Jan 20 Javascript
jquery中插件实现自动添加用户的具体代码
Nov 15 Javascript
JavaScript中实现异步编程模式的4种方法
Sep 24 Javascript
5个数组Array方法: indexOf、filter、forEach、map、reduce使用实例
Jan 29 Javascript
JavaScript中停止执行setInterval和setTimeout事件的方法
May 14 Javascript
简述AngularJS的控制器的使用
Jun 16 Javascript
jQuery树形控件zTree使用小结
Aug 02 Javascript
vue引入axios同源跨域问题
Sep 27 Javascript
详解promise.then,process.nextTick, setTimeout 以及 setImmediate的执行顺序
Nov 21 Javascript
使用vue-cli脚手架工具搭建vue-webpack项目
Jan 14 Javascript
JavaScript复制变量三种方法实例详解
Jan 09 Javascript
jQuery三组基本动画与自定义动画操作实例总结
May 09 jQuery
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标签云的实现代码
2012/10/10 PHP
PHP获取指定日期是星期几的实现方法
2016/11/30 PHP
利用Laravel事件系统如何实现登录日志的记录详解
2017/05/20 PHP
PHP7 新增常量
2021/03/09 PHP
关于JavaScript中string 的replace
2013/04/12 Javascript
JS两种定义方式的区别、内部原理
2013/11/21 Javascript
JavaScript把数组作为堆栈使用的方法
2015/03/20 Javascript
JQuery限制复选框checkbox可选中个数的方法
2015/04/20 Javascript
Javascript中arguments和arguments.callee的区别浅析
2015/04/24 Javascript
学习JavaScript事件流和事件处理程序
2016/01/25 Javascript
JavaScript中Number对象的toFixed() 方法详解
2016/09/02 Javascript
Boostrap基础教程之JavaScript插件篇
2016/09/08 Javascript
JSON 对象未定义错误的解决方法
2016/09/29 Javascript
JS异步加载的三种实现方式
2017/03/16 Javascript
完美解决UI-Grid表格元素中多个空格显示为一个空格的问题
2017/04/25 Javascript
jQuery Ajax使用FormData上传文件和其他数据后端web.py获取
2017/06/11 jQuery
微信小程序视图template模板引用的实例详解
2017/09/20 Javascript
js实现动态改变radio状态的方法
2018/02/28 Javascript
从Vuex中取出数组赋值给新的数组,新数组push时报错的解决方法
2018/09/18 Javascript
JavaScript的查询机制LHS和RHS解析
2019/08/16 Javascript
js实现日历
2020/11/07 Javascript
JavaScript Dom实现轮播图原理和实例
2021/02/19 Javascript
JavaScript 中的执行上下文和执行栈实例讲解
2021/02/25 Javascript
详解Python匿名函数(lambda函数)
2019/04/19 Python
Python高级特性之闭包与装饰器实例详解
2019/11/19 Python
Pycharm中import torch报错的快速解决方法
2020/03/05 Python
萨克斯第五大道的折扣店:Saks Fifth Avenue OFF 5TH
2016/08/25 全球购物
全球独特生活方式产品和礼品购物网站:AHAlife
2018/09/18 全球购物
什么是继承
2013/12/07 面试题
公司周年庆典邀请函
2014/01/12 职场文书
党委书记个人检查对照材料思想汇报
2014/10/11 职场文书
2014年班主任德育工作总结
2014/12/05 职场文书
财务负责人岗位职责
2015/02/03 职场文书
文明礼仪倡议书
2015/04/28 职场文书
2016党员学习作风建设心得体会
2016/01/21 职场文书
python自动计算图像数据集的RGB均值
2021/06/18 Python