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 相关文章推荐
javascript 拖动表格行实现代码
May 05 Javascript
Jquery中的层次选择器与find()的区别示例介绍
Feb 20 Javascript
js实现多张图片延迟加载效果
Jul 17 Javascript
详解如何优雅地在React项目中使用Redux
Dec 28 Javascript
用Node提供静态文件服务的方法
Jul 06 Javascript
在Node.js下运用MQTT协议实现即时通讯及离线推送的方法
Jan 24 Javascript
微信小程序模板消息限制实现无限制主动推送的示例代码
Aug 27 Javascript
刷新页面后让控制台的js代码继续执行
Sep 20 Javascript
javascript的delete运算符知识点总结
Nov 19 Javascript
js实现左右轮播图
Jan 09 Javascript
JavaScript实现与web通信的方法详解
Aug 07 Javascript
JavaScript控制台的更多功能
Apr 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实现二维数组去重功能示例
2017/01/12 PHP
PHP addslashes()函数讲解
2019/02/03 PHP
用js判断用户浏览器是否是XP SP2的IE6
2007/03/08 Javascript
jquery下利用jsonp跨域访问实现方法
2010/07/29 Javascript
js实现在文本框光标处添加字符的方法介绍
2012/11/24 Javascript
基于jquery的网站幻灯片切换效果焦点图代码
2013/09/15 Javascript
js怎么覆盖原有方法实现重写
2014/09/04 Javascript
深入了解JavaScript中的Symbol的使用方法
2015/07/28 Javascript
JavaScript位置与大小(1)之正确理解和运用与尺寸大小相关的DOM属性
2015/12/26 Javascript
WebGL利用FBO完成立方体贴图效果完整实例(附demo源码下载)
2016/01/26 Javascript
jQuery插件 Jqplot图表实例
2016/06/18 Javascript
AngularJS入门教程之路由与多视图详解
2016/08/19 Javascript
jQuery实现可移动选项的左右下拉列表示例
2016/12/26 Javascript
简单谈谈Javascript函数中的arguments
2017/02/09 Javascript
JavaScript脚本语言是什么_动力节点Java学院整理
2017/06/26 Javascript
Angular之toDoList的实现代码示例
2017/12/02 Javascript
jQuery 导航自动跟随滚动的实现代码
2018/05/30 jQuery
利用pyinstaller或virtualenv将python程序打包详解
2017/03/22 Python
Python实现按学生年龄排序的实际问题详解
2017/08/29 Python
django DRF图片路径问题的解决方法
2018/09/10 Python
python实现名片管理系统项目
2019/04/26 Python
python 怎样将dataframe中的字符串日期转化为日期的方法
2019/09/26 Python
python通过对字典的排序,对json字段进行排序的实例
2020/02/27 Python
sublime3之内网安装python插件Anaconda的流程
2020/11/10 Python
如何用 Python 制作 GitHub 消息助手
2021/02/20 Python
html5视频常用API接口的实战示例
2020/03/20 HTML / CSS
白色公司:The White Company
2017/10/11 全球购物
日本最大的旅游网站:Rakuten Travel(乐天旅游)
2018/08/02 全球购物
大专生工程监理求职信
2013/10/04 职场文书
客户经理竞聘演讲稿
2014/05/15 职场文书
美食节策划方案
2014/05/26 职场文书
社团活动总结怎么写
2014/06/30 职场文书
贷款工资证明范本
2015/06/12 职场文书
护理培训心得体会
2016/01/22 职场文书
技术入股协议书
2016/03/22 职场文书
一次SQL查询优化原理分析(900W+数据从17s到300ms)
2022/06/10 SQL Server