JS中的二叉树遍历详解


Posted in Javascript onMarch 18, 2016

二叉树是由根节点,左子树,右子树组成,左子树和友子树分别是一个二叉树。
这篇文章主要在JS中实现二叉树的遍历。

一个二叉树的例子

var tree = {
 value: 1,
 left: {
  value: 2,
  left: {
   value: 4
  }
 },
 right: {
  value: 3,
  left: {
   value: 5,
   left: {
    value: 7
   },
   right: {
    value: 8
   }
  },
  right: {
   value: 6
  }
 }
}

广度优先遍历
广度优先遍历是从二叉树的第一层(根结点)开始,自上至下逐层遍历;在同一层中,按照从左到右的顺序对结点逐一访问。
实现:
<!--more-->
使用数组模拟队列。首先将根节点归入队列。当队列不为空的时候,执行循环:取出队列的一个节点,如果该结点的左子树为非空,则将该结点的左子树入队列;如果该结点的右子树为非空,则将该结点的右子树入队列。
(描述有点不清楚,直接看代码吧。)

var levelOrderTraversal = function(node) { 
 if(!node) {  
  throw new Error('Empty Tree')
 } 
 var que = []
 que.push(node) 
 while(que.length !== 0) {
  node = que.shift()  
  console.log(node.value)  
  if(node.left) que.push(node.left)  
  if(node.right) que.push(node.right)
 }
}

递归遍历
觉得用这几个字母表示递归遍历的三种方法不错:
D:访问根结点,L:遍历根结点的左子树,R:遍历根结点的右子树。
先序遍历:DLR
中序遍历:LDR
后序遍历:LRD

顺着字母表示的意思念下来就是遍历的顺序了 ^ ^

这3种遍历都属于递归遍历,或者说深度优先遍历(Depth-First Search,DFS),因为它总
是优先往深处访问。

先序遍历的递归算法:

var preOrder = function (node) { 
 if (node) {  
  console.log(node.value);
  preOrder(node.left);
  preOrder(node.right);
 }
}

中序遍历的递归算法:

var inOrder = function (node) { 
 if (node) {
  inOrder(node.left);  
  console.log(node.value);
  inOrder(node.right);
 }
}

后序遍历的递归算法:

var postOrder = function (node) { 
 if (node) {
  postOrder(node.left);
  postOrder(node.right);  
  console.log(node.value);
 }
}

非递归深度优先遍历
其实对于这些概念谁是属于谁的我也搞不太清楚。有的书里将二叉树的遍历只讲了上面三种递归遍历。有的分广度优先遍历和深度优先遍历两种,把递归遍历都分入深度遍历当中;有的分递归遍历和非递归遍历两种,非递归遍历里包括广度优先遍历和下面这种遍历。个人觉得怎么分其实并不重要,掌握方法和用途就好 :)

刚刚在广度优先遍历中使用的是队列,相应的,在这种不递归的深度优先遍历中我们使用栈。在JS中还是使用一个数组来模拟它。
这里只说先序的:
额,我尝试了描述这个算法,然而并描述不清楚,按照代码走一边你就懂了。

var preOrderUnRecur = function(node) { 
 if(!node) {  
  throw new Error('Empty Tree')
 } 
 var stack = []
 stack.push(node) 
 while(stack.length !== 0) {
  node = stack.pop()  
  console.log(node.value)  
  if(node.right) stack.push(node.right)  
  if(node.left) stack.push(node.left)
 }
}

看了这一篇,找到了非递归后序的算法,所以在这里把非递归的遍历方法补充完整。
非递归中序
先把数的左节点推入栈,然后取出,再推右节点。

var inOrderUnRecur = function(node) { 
 if(!node) {  
  throw new Error('Empty Tree')
 } 
 var stack = [] 
 while(stack.length !== 0 || node) {  
  if(node) {
   stack.push(node)
   node = node.left
  } else {
   node = stack.pop()   
   console.log(node.value)
   node = node.right
  }
 }
}

非递归后序(使用一个栈)
这里使用了一个临时变量记录上次入栈/出栈的节点。思路是先把根节点和左树推入栈,然后取出左树,再推入右树,取出,最后取跟节点。

var posOrderUnRecur = function(node) { 
 if(!node) {  
  throw new Error('Empty Tree')
 } 
 var stack = []
 stack.push(node) 
 var tmp = null
 while(stack.length !== 0) {
  tmp = stack[stack.length - 1]  
  if(tmp.left && node !== tmp.left && node !== tmp.right) {
   stack.push(tmp.left)
  } else if(tmp.right && node !== tmp.right) {
   stack.push(tmp.right)
  } else {   
   console.log(stack.pop().value)
   node = tmp
  }
 }
}

非递归后序(使用两个栈)
这个算法的思路和上面那个差不多,s1有点像一个临时变量。

var posOrderUnRecur = function(node) { 
 if(node) {  
  var s1 = []  
  var s2 = []
  s1.push(node)  
  while(s1.length !== 0) {
   node = s1.pop()
   s2.push(node)   
   if(node.left) {
    s1.push(node.left)
   }   
   if(node.right) {
    s1.push(node.right)
   }
  }  
  while(s2.length !== 0) {   
   console.log(s2.pop().value);
  }
 }
}

Morris遍历
这个方法即不用递归也不用栈实现三种深度遍历,空间复杂度为O(1)(这个概念我也不是特别清楚org)
(这三种算法我先放着,有空再研究)
Morris先序:

var morrisPre = function(head) { 
 if(!head) {  
  return
 } 
 var cur1 = head,
   cur2 = null
 while(cur1) {
  cur2 = cur1.left  
  if(cur2) {   
   while(cur2.right && cur2.right != cur1) {
    cur2 = cur2.right
   }   
   if(!cur2.right) {
    cur2.right = cur1    
    console.log(cur1.value)
    cur1 = cur1.left    
    continue
   } else {
    cur2.right = null
   }
  } else {   
    console.log(cur1.value)
  }
  cur1 = cur1.right
 }
}

Morris中序:

var morrisIn = function(head) { 
 if(!head) {  
  return
 } 
 var cur1 = head,
   cur2 = null
 while(cur1) {
  cur2 = cur1.left  
  if(cur2) {   
   while(cur2.right && cur2.right !== cur1) {
    cur2 = cur2.right
   }   
   if(!cur2.right) {
    cur2.right = cur1
    cur1 = cur1.left    
    continue
   } else {
    cur2.right = null
   }
  }  
  console.log(cur1.value)
  cur1 = cur1.right
 }
}

Morris后序:

var morrisPost = function(head) { 
 if(!head) {  
  return
 } 
 var cur1 = head,
   cur2 = null
 while(cur1) {
  cur2 = cur1.left  
  if(cur2) {   
   while(cur2.right && cur2.right !== cur1) {
    cur2 = cur2.right
   }   
   if(!cur2.right) {
    cur2.right = cur1
    cur1 = cur1.left    
    continue
   } else {
    cur2.right = null
    printEdge(cur1.left)
   }
  }
  cur1 = cur1.right
 }
 printEdge(head)
}
var printEdge = function(head) {

以上就是本文的全部内容,希望对大家的学习有所帮助。

Javascript 相关文章推荐
jquery限制输入字数,并提示剩余字数实现代码
Dec 24 Javascript
jquery click([data],fn)使用方法实例介绍
Jul 08 Javascript
javascript获取鼠标位置部分的实例代码(兼容IE,FF)
Aug 05 Javascript
js 控制图片大小核心讲解
Oct 09 Javascript
Adapter适配器模式在JavaScript设计模式编程中的运用分析
May 18 Javascript
JS继承之借用构造函数继承和组合继承
Sep 07 Javascript
基于jQuery实现左侧菜单栏可折叠功能
Dec 27 Javascript
angular中的http拦截器Interceptors的实现
Feb 21 Javascript
原生js二级联动效果
Jun 20 Javascript
postman+json+springmvc测试批量添加实例
Mar 31 Javascript
mac上配置Android环境变量的方法
Jul 08 Javascript
JavaScript图片旋转效果实现方法详解
Jun 28 Javascript
简述JavaScript提交表单的方式 (Using JavaScript Submit Form)
Mar 18 #Javascript
Javascript的表单验证-揭开正则表达式的面纱
Mar 18 #Javascript
Javascript的表单验证-初识正则表达式
Mar 18 #Javascript
Javascript的表单验证-提交表单
Mar 18 #Javascript
Javascript的表单与验证-非空验证
Mar 18 #Javascript
悬浮广告方法日常收集整理
Mar 18 #Javascript
Js与Jq获取浏览器和对象值的方法
Mar 18 #Javascript
You might like
php访问查询mysql数据的三种方法
2006/10/09 PHP
PHP 八种基本的数据类型小结
2011/06/01 PHP
PHP页面实现定时跳转的方法
2014/10/31 PHP
Yii中实现处理前后台登录的新方法
2015/12/28 PHP
全面解析PHP面向对象的三大特征
2017/06/10 PHP
PHP生成指定范围内的N个不重复的随机数
2019/03/18 PHP
原生Js与jquery的多组处理, 仅展开一个区块的折叠效果
2011/01/09 Javascript
JS命名空间的另一种实现
2013/08/09 Javascript
JavaScript实现找质数代码分享
2015/03/24 Javascript
jquery制作多功能轮播图插件
2015/04/02 Javascript
js闭包实现按秒计数
2015/04/23 Javascript
js上传图片及预览功能实例分析
2015/04/24 Javascript
jQuery的promise与deferred对象在异步回调中的作用
2016/05/03 Javascript
微信小程序自动客服功能
2017/11/02 Javascript
JS实现不用中间变量temp 实现两个变量值得交换方法
2018/02/04 Javascript
Vue路由钩子之afterEach beforeEach的区别详解
2018/07/15 Javascript
JS表格的动态操作完整示例
2020/01/13 Javascript
微信小程序自定义弹出模态框禁止底部滚动功能
2020/03/09 Javascript
TypeScript的安装、使用、自动编译的实现
2020/04/10 Javascript
[02:52]DOTA2新手基础教程 米波
2014/01/21 DOTA
PyTorch和Keras计算模型参数的例子
2020/01/02 Python
python 读取二进制 显示图片案例
2020/04/24 Python
Python中使用Selenium环境安装的方法步骤
2021/02/22 Python
利用canvas实现图片压缩的示例代码
2018/07/17 HTML / CSS
高清屏中使用Canvas绘图出现模糊的问题及解决方法
2019/06/03 HTML / CSS
几道数据库的概念性面试题
2014/05/30 面试题
介绍一下Make? 为什么使用make
2013/12/08 面试题
重写子类方法时,抛出异常的书写注意事项
2015/10/17 面试题
业务代表的岗位职责
2013/11/16 职场文书
人力资源主管职责范本
2014/03/05 职场文书
安全责任书范本
2014/04/15 职场文书
2014年置业顾问工作总结
2014/11/17 职场文书
后进生评语大全
2015/01/04 职场文书
债务纠纷起诉书
2015/05/20 职场文书
2016年共产党员个人承诺书
2016/03/24 职场文书
.Net Core导入千万级数据至Mysql的步骤
2021/05/24 MySQL