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


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原型继承的陷阱
Dec 03 Javascript
JS+CSS实现精美的二级导航效果代码
Sep 17 Javascript
jQuery实现点击后高亮背景固定显示的菜单效果【附demo源码下载】
Sep 21 Javascript
JavaScript中浅讲ajax图文详解
Nov 11 Javascript
js实现表单提交后不重新刷新当前页面
Nov 30 Javascript
JavaScript基础之AJAX简单的小demo
Jan 29 Javascript
js原生Ajax的封装和原理详解
Mar 11 Javascript
使用ionic播放轮询广告的实现方法(必看)
Apr 24 Javascript
详解Vue基于 Nuxt.js 实现服务端渲染(SSR)
Apr 05 Javascript
解决vue热替换失效的根本原因
Sep 19 Javascript
JS中的函数与对象的创建方式
May 12 Javascript
js实现三角形粒子运动
Sep 22 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 命令行工具 shell_exec, exec, passthru, system详细使用介绍
2011/09/11 PHP
destoon复制新模块的方法
2014/06/21 PHP
ThinkPHP中公共函数路径和配置项路径的映射分析
2014/11/22 PHP
smarty模板引擎中变量及变量修饰器用法实例
2015/01/22 PHP
PHP的mysqli_rollback()函数讲解
2019/01/23 PHP
Thinkphp5框架中引入Markdown编辑器操作示例
2020/06/03 PHP
javascript 从if else 到 switch case 再到抽象
2010/07/17 Javascript
JavaScript中各种编码解码函数的区别和注意事项
2010/08/19 Javascript
ExtJS如何设置与获取radio控件的选取状态
2014/01/22 Javascript
分享我的jquery实现下拉菜单心的
2015/11/29 Javascript
JS简单模拟触发按钮点击功能的方法
2015/11/30 Javascript
javascript实现简易计算器的代码
2016/05/31 Javascript
json对象与数组以及转换成js对象的简单实现方法
2016/06/24 Javascript
JS中Select下拉列表类(支持输入模糊查询)功能
2017/01/17 Javascript
纯JS单页面赛车游戏制作代码分享
2017/03/03 Javascript
bootstrap multiselect 多选功能实现方法
2017/06/05 Javascript
React组件重构之嵌套+继承及高阶组件详解
2018/07/19 Javascript
JavaScript设计模式之代理模式实例分析
2019/01/16 Javascript
Python实现从log日志中提取ip的方法【正则提取】
2018/03/31 Python
python 对给定可迭代集合统计出现频率,并排序的方法
2018/10/18 Python
Python面向对象魔法方法和单例模块代码实例
2020/03/25 Python
Jupyter notebook 启动闪退问题的解决
2020/04/13 Python
如何使用Python处理HDF格式数据及可视化问题
2020/06/24 Python
使用python将微信image下.dat文件解密为.png的方法
2020/11/30 Python
python爬虫看看虎牙女主播中谁最“顶”步骤详解
2020/12/01 Python
Kiehl’s科颜氏西班牙官方网站:源自美国的植物护肤品牌
2020/02/22 全球购物
法国低价在线宠物商店:bitiba.fr
2020/07/03 全球购物
大学生实习期自我评价范文
2013/10/03 职场文书
历史学专业推荐信
2013/11/06 职场文书
小班评语大全
2014/05/04 职场文书
关于青春的演讲稿500字
2014/08/22 职场文书
作文批改评语
2014/12/25 职场文书
校运会通讯稿
2015/07/18 职场文书
2015年党务工作者个人工作总结
2015/10/22 职场文书
《三国志》赏析
2019/08/27 职场文书
导游词之清晏园
2019/11/22 职场文书