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 学习书 推荐
Jun 13 Javascript
jQuery实现点击文本框弹出热门标签的提示效果
Nov 17 Javascript
node.js中的forEach()是同步还是异步呢
Jan 29 Javascript
jQuery实现表单步骤流程导航代码分享
Aug 28 Javascript
jQuery的框架介绍
May 11 Javascript
AngularJs Javascript MVC 框架
Jun 20 Javascript
jQuery动态创建元素以及追加节点的实现方法
Oct 20 Javascript
详解JS获取HTML DOM元素的8种方法
Jun 17 Javascript
详解webpack运行Babel教程
Jun 13 Javascript
微信小程序进入广告实现代码实例
Sep 19 Javascript
Node.js 实现抢票小工具 &amp; 短信通知提醒功能
Oct 22 Javascript
微信小程序webSocket的使用方法
Feb 20 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
长波知识介绍
2021/03/01 无线电
PHP Pear 安装及使用
2009/03/19 PHP
提高PHP编程效率 引入缓存机制提升性能
2010/02/15 PHP
php中根据变量的类型 选择echo或dump
2012/07/05 PHP
php切割页面div内容的实现代码分享
2012/07/31 PHP
解析php获取字符串的编码格式的方法(函数)
2013/06/21 PHP
PHP如何实现Unicode和Utf-8编码相互转换
2015/07/29 PHP
PHP中trait使用方法详细介绍
2017/05/21 PHP
Django中通过定时任务触发页面静态化的处理方式
2018/08/29 PHP
完美解决JS中汉字显示乱码问题(已解决)
2006/12/27 Javascript
通过百度地图获取公交线路的站点坐标的js代码
2012/05/11 Javascript
JS在TextArea光标位置插入文字并实现移动光标到文字末尾
2013/06/21 Javascript
JavaScript异步回调的Promise模式封装实例
2014/06/07 Javascript
js通过location.search来获取页面传来的参数
2014/09/11 Javascript
jQuery插件AjaxFileUpload实现ajax文件上传
2016/05/05 Javascript
详解XMLHttpRequest(二)响应属性、二进制数据、监测上传下载进度
2016/09/14 Javascript
RequireJS 依赖关系的实例(推荐)
2017/01/21 Javascript
详解Angular 4.x 动态创建组件
2017/04/25 Javascript
浅谈vue中数据双向绑定的实现原理
2017/09/14 Javascript
python基础教程之分支、循环简单用法
2016/06/16 Python
Python使用迭代器打印螺旋矩阵的思路及代码示例
2016/07/02 Python
python Flask实现restful api service
2017/12/04 Python
Python使用pandas处理CSV文件的实例讲解
2018/06/22 Python
教你利用Python玩转histogram直方图的五种方法
2018/07/30 Python
利用Python批量识别电子账单数据的方法
2021/02/08 Python
HTML5 video循环播放多个视频的方法步骤
2020/08/06 HTML / CSS
欧洲最大的美妆零售网站:Feelunique
2017/01/14 全球购物
Godiva巧克力英国官网:比利时歌帝梵巧克力
2018/08/28 全球购物
size?爱尔兰官方网站:英国伦敦的球鞋精品店
2019/03/31 全球购物
技术总监岗位职责
2013/12/05 职场文书
亲子运动会的活动方案
2014/08/17 职场文书
行政执法作风整顿剖析材料
2014/10/11 职场文书
六查六看自查报告
2014/10/14 职场文书
给老婆的检讨书(搞笑版)
2015/05/06 职场文书
舞蹈社团活动总结
2015/05/07 职场文书
Python中的socket网络模块介绍
2022/07/23 Python