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或asp实现的判断身份证号码是否正确两种验证方法
Nov 26 Javascript
超级24小时弹窗代码 24小时退出弹窗代码 100%弹窗代码(IE only)
Jun 11 Javascript
验证javascript中Object和Function的关系的三段简单代码
Jun 27 Javascript
基于jQuery的倒计时插件代码
May 07 Javascript
详解基于Bootstrap扁平化的后台框架Ace
Nov 27 Javascript
JS针对浏览器窗口关闭事件的监听方法集锦
Jun 24 Javascript
基于touch.js手势库+zepto.js插件开发图片查看器(滑动、缩放、双击缩放)
Nov 17 Javascript
vue双向绑定的简单实现
Dec 22 Javascript
基于elementUI实现图片预览组件的示例代码
Mar 31 Javascript
vue响应式系统之observe、watcher、dep的源码解析
Apr 09 Javascript
微信小程序实现watch监听
Jun 04 Javascript
vue引入静态js文件的方法
Jun 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
PHP实现多服务器session共享之NFS共享的方法
2007/03/16 PHP
php中strlen和mb_strlen用法实例分析
2016/11/12 PHP
Javascript 二维数组
2009/11/26 Javascript
web的各种前端打印方法之jquery打印插件jqprint实现网页打印
2013/01/09 Javascript
javascript 实现字符串反转的三种方法
2013/11/23 Javascript
仿JQuery输写高效JSLite代码的一些技巧
2015/01/13 Javascript
JS实现超炫网页烟花动画效果的方法
2015/03/02 Javascript
Js实现自定义右键行为
2015/03/26 Javascript
js注入 黑客之路必备!
2016/09/14 Javascript
深入对Vue.js $watch方法的理解
2017/03/20 Javascript
Layui组件Table绑定行点击事件和获取行数据的方法
2018/08/19 Javascript
jquery+php后台实现省市区联动功能示例
2019/05/23 jQuery
JS实现灯泡开关特效
2020/03/30 Javascript
Vue实现点击当前元素以外的地方隐藏当前元素(实现思路)
2019/12/04 Javascript
在Vuex中Mutations修改状态操作
2020/07/24 Javascript
python的id()函数介绍
2013/02/10 Python
Python中for循环详解
2014/01/17 Python
pycharm 使用心得(六)进行简单的数据库管理
2014/06/06 Python
python数据类型_字符串常用操作(详解)
2017/05/30 Python
Python编程之黑板上排列组合,你舍得解开吗
2017/10/30 Python
PyQt5每天必学之日历控件QCalendarWidget
2018/04/19 Python
tensorflow 用矩阵运算替换for循环 用tf.tile而不写for的方法
2018/07/27 Python
python散点图实例之随机漫步
2018/08/27 Python
对python GUI实现完美进度条的示例详解
2018/12/13 Python
python ddt数据驱动最简实例代码
2019/02/22 Python
详解python的四种内置数据结构
2019/03/19 Python
详解Python3之数据指纹MD5校验与对比
2019/06/11 Python
python 使用socket传输图片视频等文件的实现方式
2019/08/07 Python
Python浮点数四舍五入问题的分析与解决方法
2019/11/19 Python
秋季运动会活动方案
2014/02/05 职场文书
可口可乐广告词
2014/03/20 职场文书
学习型党组织建设经验材料
2014/05/26 职场文书
12.4全国法制宣传日活动总结
2014/11/01 职场文书
导游词之西湖雷峰塔
2019/09/18 职场文书
一篇文章弄懂Python关键字、标识符和变量
2021/07/15 Python
Java实现超大Excel文件解析(XSSF,SXSSF,easyExcel)
2022/07/15 Java/Android