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 相关文章推荐
IE中jscript/javascript的条件编译
Sep 07 Javascript
IE Firefox 使用自定义标签的区别
Oct 15 Javascript
JavaScript判断窗口是否最小化的代码(跨浏览器)
Aug 01 Javascript
JqGrid web打印实现代码
May 31 Javascript
jQuery解决下拉框select设宽度时IE 6/7/8下option超出显示不全
May 27 Javascript
VUEJS实战之修复错误并且美化时间(2)
Jun 13 Javascript
通过JS和PHP两种方法判断用户请求时使用的浏览器类型
Sep 01 Javascript
关于Sequelize连接查询时inlude中model和association的区别详解
Feb 27 Javascript
angular中子控制器向父控制器传值的实例
Oct 08 Javascript
微信小程序基于Taro的分享图片功能实践详解
Jul 12 Javascript
vue 导航菜单刷新状态不消失,显示对应的路由界面操作
Aug 06 Javascript
Javascript的promise,async和await的区别详解
Mar 24 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的FTP学习(二)
2006/10/09 PHP
php的curl实现get和post的代码
2008/08/23 PHP
php调用mysql数据 dbclass类
2011/05/07 PHP
ThinkPHP调用百度翻译类实现在线翻译
2014/06/26 PHP
学习php设计模式 php实现桥梁模式(bridge)
2015/12/07 PHP
php操纵mysqli数据库的实现方法
2016/09/18 PHP
PHP去除字符串最后一个字符的三种方法实例
2017/03/01 PHP
一个基于jquery的图片切换效果
2010/07/06 Javascript
基于jQuery的Spin Button自定义文本框数值自增或自减
2010/07/17 Javascript
javascript:void(0)使用探讨
2013/08/27 Javascript
jQuery学习笔记之 Ajax操作篇(三) - 过程处理
2014/06/23 Javascript
我的Node.js学习之路(二)NPM模块管理
2014/07/06 Javascript
JavaScript原生节点操作小结
2017/01/17 Javascript
Javascript基础回顾之(一) 类型
2017/01/31 Javascript
理解nodejs的stream和pipe机制的原理和实现
2017/08/12 NodeJs
利用JS测试目标网站的打开响应速度
2017/12/01 Javascript
vue项目中使用bpmn-自定义platter的示例代码
2020/05/11 Javascript
Python isinstance判断对象类型
2008/09/06 Python
python常用web框架简单性能测试结果分享(包含django、flask、bottle、tornado)
2014/08/25 Python
使用Python中的cookielib模拟登录网站
2015/04/09 Python
Python中的Descriptor描述符学习教程
2016/06/02 Python
Python中Django 后台自定义表单控件
2017/03/28 Python
python数据处理实战(必看篇)
2017/06/11 Python
numpy concatenate数组拼接方法示例介绍
2019/05/27 Python
利用Python绘制有趣的万圣节南瓜怪效果
2019/10/31 Python
常用python爬虫库介绍与简要说明
2020/01/25 Python
html5 postMessage前端跨域并前端监听的方法示例
2018/11/01 HTML / CSS
Homestay中文官网:全球寄宿家庭
2018/10/18 全球购物
奥地利票务门户网站:oeticket.com
2019/12/31 全球购物
Skechers越南官方网站:来自美国的运动休闲品牌
2021/02/22 全球购物
网络工程专业毕业生推荐信
2013/10/28 职场文书
拓展策划方案
2014/06/03 职场文书
2015年元旦演讲稿
2014/09/12 职场文书
2014党的群众路线教育实践活动总结报告
2014/10/31 职场文书
python xlwt模块的使用解析
2021/04/13 Python
分布式架构Redis中有哪些数据结构及底层实现原理
2022/03/13 Redis