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


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 相关文章推荐
jquery的index方法实现tab效果
Feb 16 Javascript
JavaScript初学者应注意的七个细节小结
Jan 30 Javascript
关于jQuery $.isNumeric vs. $.isNaN vs. isNaN
Apr 15 Javascript
浅析Bootstrap验证控件的使用
Jun 23 Javascript
JavaScript实现广告弹窗效果
Aug 09 Javascript
图片懒加载插件实例分享(含解析)
Jan 09 Javascript
JQuery实现文字无缝滚动效果示例代码(Marquee插件)
Mar 07 Javascript
利用n工具轻松管理Node.js的版本
Apr 21 Javascript
ztree简介_动力节点Java学院整理
Jul 19 Javascript
vue2里面ref的具体使用方法
Oct 27 Javascript
解决Vue打包之后文件路径出错的问题
Mar 06 Javascript
layui实现图片虚拟路径上传,预览和删除的例子
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计算当前程序执行时间示例
2014/04/24 PHP
PHP模板引擎Smarty内建函数section,sectionelse用法详解
2016/04/11 PHP
Yii2中DropDownList简单用法示例
2016/07/18 PHP
解决Laravel5.x的php artisan migrate数据库迁移创建操作报错SQLSTATE[42000]
2020/04/06 PHP
获取当前网页document.url location.href区别总结
2008/05/10 Javascript
img标签中onerror用法
2009/08/13 Javascript
js的一些常用方法小结
2011/06/29 Javascript
利用window.name实现windowStorage代码分享
2014/01/02 Javascript
javascript获取wx.config内部字段解决微信分享
2016/03/09 Javascript
Angularjs手动解析表达式($parse)
2016/10/12 Javascript
全屏滚动插件fullPage.js使用实例解析
2016/10/21 Javascript
微信开发 使用picker封装省市区三级联动模板
2016/10/28 Javascript
微信小程序 实例开发总结
2017/04/26 Javascript
使用node.js实现微信小程序实时聊天功能
2018/08/13 Javascript
微信小程序swiper实现文字纵向轮播提示效果
2020/01/21 Javascript
vue页面更新patch的实现示例
2020/03/25 Javascript
[01:05:40]VG vs Newbee 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
[01:07:57]DOTA2-DPC中国联赛 正赛 Ehome vs Magma BO3 第二场 1月19日
2021/03/11 DOTA
python通过wxPython打开一个音频文件并播放的方法
2015/03/25 Python
Python 制作糗事百科爬虫实例
2016/09/22 Python
Python命令行参数解析工具 docopt 安装和应用过程详解
2019/09/26 Python
pytorch实现线性拟合方式
2020/01/15 Python
MxNet预训练模型到Pytorch模型的转换方式
2020/05/25 Python
基于python的opencv图像处理实现对斑马线的检测示例
2020/11/29 Python
美国鲜花递送:UrbanStems
2021/01/04 全球购物
《小动物过冬》教学反思
2014/04/17 职场文书
模范教师材料大全
2014/12/16 职场文书
以权谋私检举信范文
2015/03/02 职场文书
2015年学校禁毒工作总结
2015/05/27 职场文书
生死牛玉儒观后感
2015/06/11 职场文书
欢迎新生标语2015
2015/07/16 职场文书
2015年秋季校长开学典礼致辞
2015/07/29 职场文书
一篇带你入门Java垃圾回收器
2021/06/16 Java/Android
Netty分布式客户端处理接入事件handle源码解析
2022/03/25 Java/Android
Python使用openpyxl模块处理Excel文件
2022/06/05 Python
Java中的Kotlin 内部类原理
2022/06/16 Java/Android