资讯专栏INFORMATION COLUMN

JS中的二叉树遍历

ghnor / 1674人阅读

摘要:一个二叉树的例子广度优先遍历广度优先遍历是从二叉树的第一层根结点开始,自上至下逐层遍历在同一层中,按照从左到右的顺序对结点逐一访问。有的书里将二叉树的遍历只讲了上面三种递归遍历。

二叉树是由根节点,左子树,右子树组成,左子树和友子树分别是一个二叉树。
这篇文章主要在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
    }
  }
}
广度优先遍历

广度优先遍历是从二叉树的第一层(根结点)开始,自上至下逐层遍历;在同一层中,按照从左到右的顺序对结点逐一访问。
实现:

使用数组模拟队列。首先将根节点归入队列。当队列不为空的时候,执行循环:取出队列的一个节点,如果该结点的左子树为非空,则将该结点的左子树入队列;如果该结点的右子树为非空,则将该结点的右子树入队列。
(描述有点不清楚,直接看代码吧。)

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)
  }
}

看了LK的这一篇,找到了非递归后序的算法(之前没写就是因为这种实在不会啊啊啊),所以在这里把非递归的遍历方法补充完整。
非递归中序
先把数的左节点推入栈,然后取出,再推右节点。(我能说出的描述就如此了~~)

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) {
  var tail = reverseEdge(head)
  var cur = tail
  while(cur) {
    console.log(cur.value)
    cur = cur.right
  }
  reverseEdge(tail)
}

var reverseEdge = function(head) {
  var pre = null,
      next = null
  while(head) {
    next = head.right
    head.right = pre
    pre = head
    head = next
  }
  return pre
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78922.html

相关文章

  • js数据结构和算法(三)叉树

    摘要:同样结点树的二叉树,完全二叉树的深度最小。二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域是比较自然的想法,我们称这样的链表叫做二叉链表。 二叉树的概念 二叉树(Binary Tree)是n(n>=0)个结点的有限集合,该集合或者为空集(空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。 showImg(https://seg...

    DesGemini 评论0 收藏0
  • js叉树的深度遍历与广度遍历(递归实现与非递归实现)

    摘要:树中结点的最大层次称为树的深度或高度。二叉树有深度遍历和广度遍历,深度遍历有前序中序和后序三种遍历方法。二叉树的前序遍历可以用来显示目录结构等中序遍历可以实现表达式树,在编译器底层很有用后序遍历可以用来实现计算目录内的文件及其信息等。 树的简介 栈、队列、链表等数据结构,都是顺序数据结构。而树是非顺序数据结构。树型结构是一类非常重要的非线性结构。直观地,树型结构是以分支关系定义的层次结...

    Yuanf 评论0 收藏0
  • 数据结构与算法:叉树算法

    摘要:因此,根据题目给出的先序遍历和中序遍历,可以画出二叉树选参考数据结构与算法描述实现二叉树算法浅谈数据结构二叉树慕课网实现二叉树算法前端树控件腾讯软件开发面试题 内容衔接上一章 数据结构与算法:常见排序算法 内容提要 什么是树   - 为什么使用树 二叉树 二叉查找树 红黑树 B、B+树 堆 伸展树 树 可以点击链接感受下笔者用d3.js画的tree https://codepen...

    Little_XM 评论0 收藏0
  • js数据结构-叉树二叉搜索树)

    摘要:前言可能有一部分人没有读过我上一篇写的二叉堆,所以这里把二叉树的基本概念复制过来了,如果读过的人可以忽略前面针对二叉树基本概念的介绍,另外如果对链表数据结构不清楚的最好先看一下本人之前写的数据结构链表二叉树二叉树是一种树形结构,它的特点是 前言 可能有一部分人没有读过我上一篇写的二叉堆,所以这里把二叉树的基本概念复制过来了,如果读过的人可以忽略前面针对二叉树基本概念的介绍,另外如果对链...

    codeKK 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<