资讯专栏INFORMATION COLUMN

推导二叉树的遍历结果

joy968 / 1823人阅读

摘要:推导前序序列已知二叉树的中序序列是,后序序列是,求前序序列。二叉树的中序序列是按照左子树,根,右子树的顺序排列的,根将左子树的序列和右子树的序列分割在左右两边。

推导前序序列

已知二叉树的中序序列是ABCDEFG,后序序列是BDCAFGE,求前序序列。

思路

二叉树的后序序列是按照「左子树」,「右子树」,「根」的顺序排列的,序列中最后一个元素代表该二叉树的根节点。
二叉树的前序序列是按照「根」,「左子树」,「右子树」的顺序排列的,序列中第一个元素代表该二叉树的根节点。故通过已知的后序序列可以发现根,作为前序序列的第一个元素。
二叉树的中序序列是按照「左子树」,「根」,「右子树」的顺序排列的,根将左子树的序列和右子树的序列分割在左右两边。通过后序序列发现根,找到其在中序序列的位置,从而在中序序列中发现左子树和右子树并获得它们的长度,最后在后序序列中找到对应的左子树和右子树的后序序列。
这样求某二叉树的前序序列,变成了求该二叉树左子树的前序序列和该二叉树右子树的前序序列。

递归解法

一棵树,先找到其根,将其压入栈中,再将其分割成左右子树,按左子树,右子树的顺序,找子树的根,将其压入栈中,直到子树不可分割,得到整棵树的前序序列。

/**
 * 返回二叉树的前序序列
 * @param {array} inOrder 
 * @param {array} postOrder 
 * @returns {array} preOrder
 */
function findPreOrder (inOrder, postOrder) {
  let preOrder = []
  // 保存前序序列
  !function findRoot(inOrder, postOrder) {
    if (inOrder.length <= 1) { 
      // 若序列的长度为0或1,停止递归调用
      inOrder[0] && preOrder.push(inOrder[0]) 
      // 若序列长度为1,则它就是该树的根节点,根入栈
      // 在先序序列中,树的根排列顺序先于子树的根
      return
    } else {
      const root = postOrder[postOrder.length - 1] 
      // 找到根节点
      preOrder.push(root) 
      // 根入栈
      const index = inOrder.indexOf(root) 
      // 找到根节点在中序序列中的位置
      const newInOrderLeft = inOrder.slice(0, index) 
      // 根据根的位置找出左子树的中序序列
      const newInOrderRight = inOrder.slice(index + 1) 
      // 根据根的位置找出右子树的中序序列
      const newPostOrderLeft = postOrder.slice(0, newInOrderLeft.length) 
      // 根据左子树中序序列的长度找出其后序序列
      const newPostOrderRight = postOrder.slice(newInOrderLeft.length, newInOrderRight.length + newInOrderLeft.length) 
      // 根据右子树中序序列的长度找出其后序序列
      findRoot(newInOrderLeft, newPostOrderLeft)
      // 找到左子树的根
      findRoot(newInOrderRight, newPostOrderRight)
      // 找到右子树的根
      // 在先序序列中,左子树的根排列顺序先于右子树的根
    }
  }(inOrder, postOrder)
  return preOrder
  // 返回前序序列
}

let preOrder = findPreOrder(Array.from("ABCDEFG"), Array.from("BDCAFGE"))
console.log(preOrder)
// [ "E", "A", "C", "B", "D", "G", "F" ]
非递归解法

非递归解法和递归解法的思路相同,找到根,压入栈,分割左右子树,找到它们的根,压入栈。
不同之处在于用inOrderArr保存待分割的树的中序序列,用postOrderArr保存待分割的树的后序序列。

const findPreOrder = (inOrder, postOrder) => {
  let preOrder = [],
  // 保存前序序列
    inOrderArr = [inOrder],
    // 存储待分割的树的中序序列,初始状态为传入的树的中序序列
    postOrderArr = [postOrder]
    // 存储待分割的树的后序序列,初始状态为传入的树的后序序列
  while (preOrder.length < inOrder.length) {
    let inOrderEle = inOrderArr.shift(),
    // 取出子树的中序序列
      postOrderEle = postOrderArr.shift()
      // 取出子树的后序序列
    if (inOrderEle.length <= 1) {
    // 若取出的序列的长度为0或1,就不再分割
      inOrderEle[0] && preOrder.push(inOrderEle[0])
      // 若取出的序列的长度为1,则为根,入栈
    } else {
      let root = postOrderEle[postOrderEle.length - 1]
      // 找到根
      preOrder.push(root)
      // 根入栈
      // 开始分割左右子树
      const index = inOrderEle.indexOf(root)
      const newinOrderEle1 = inOrderEle.slice(0, index)
      const newinOrderEle2 = inOrderEle.slice(index + 1)
      inOrderArr = [newinOrderEle1, newinOrderEle2, ...inOrderArr] // 用分割后的左右子树代替原先的树
      const newpostOrderEle1 = postOrderEle.slice(0, newinOrderEle1.length)
      const newpostOrderEle2 = postOrderEle.slice(newinOrderEle1.length, newinOrderEle1.length + newinOrderEle2.length)
      postOrderArr = [newpostOrderEle1, newpostOrderEle2, ...postOrderArr] // 用分割后的左右子树代替原先的树
    }
  }
  return preOrder
  // 返回前序序列
}

let preOrder = findPreOrder(Array.from("ABCDEFG"), Array.from("BDCAFGE"))
console.log(preOrder)
// [ "E", "A", "C", "B", "D", "G", "F" ]
推导后序序列

已知二叉树的前序序列是EACBDGF,中序序列是ABCDEFG,求后序序列。

思路

通过前序序列找到根,找到根在中序序列的位置,将树分割成左右子树,按照先右子树再左子树的顺序找到子树的根,直至子树不可分割。先找到的根排在后找到的根的后面。
和推导前序序列,相比思路大致相同,实现细节上,找树的根的方式不同,分割子树的方式不同,保存根的方式不同。

递归解法
const findPostOrder = (preOrder, inOrder) => {
  let postOrder = []
  // 保存后序序列
  !function findRoot(preOrder, inOrder) {
    if(inOrder.length <= 1){
    // 若序列的长度为0或1,停止递归调用
      inOrder[0] && postOrder.unshift(inOrder[0])
      // 若序列长度为1,则它就是该树的根节点,根入栈
      // 在后序序列中,树的根排列顺序后于子树的根
      return
    }else{
      const root = preOrder[0]
      // 找到根节点
      postOrder.unshift(root)
      // 保存根
      const index = inOrder.indexOf(root)
      // 找到根节点在中序序列中的位置
      const newInOrderLeft = inOrder.slice(0, index)
      // 根据根的位置找出左子树的中序序列
      const newInOrderRight = inOrder.slice(index + 1)
      // 根据根的位置找出右子树的中序序列
      const newPreOrderLeft = preOrder.slice(1, newInOrderLeft.length + 1)
      // 根据左子树中序序列的长度找出其前序序列
      const newPreOrderRight = preOrder.slice(newInOrderLeft.length + 1)
      // 根据右子树中序序列的长度找出其前序序列
      findRoot(newPreOrderRight, newInOrderRight)
      // 找到右子树的根
      findRoot(newPreOrderLeft, newInOrderLeft)
      // 找到左子树的根
    }
  }(preOrder, inOrder)
  return postOrder
  // 返回后序序列
}

let postOrder = findPostOrder(Array.from("EACBDGF"), Array.from("ABCDEFG"))
console.log(postOrder)
// [ "B", "D", "C", "A", "F", "G", "E" ]
非递归解法

非递归解法和递归解法的思路相同,找到根并保存,分割左右子树,找到它们的根并保存。
不同之处在于用preOrderArr保存待分割的树的前序序列,用inOrderArr保存待分割的树的中序序列,且每次取最后一个元素开始处理。

const findPostOrder = (preOrder, inOrder) => {
  let postOrder = [],
    preOrderArr = [preOrder],
    inOrderArr = [inOrder]
  while (postOrder.length < inOrder.length) {
    let inOrderEle = inOrderArr.pop(),
      preOrderEle = preOrderArr.pop()
    if (inOrderEle.length <= 1) {
      inOrderEle[0] && postOrder.unshift(inOrderEle[0])
    } else {
      let root = preOrderEle[0]
      postOrder.unshift(root)
      const index = inOrderEle.indexOf(root)
      const newinOrderEle1 = inOrderEle.slice(0, index)
      const newinOrderEle2 = inOrderEle.slice(index + 1)
      inOrderArr = [...inOrderArr, newinOrderEle1, newinOrderEle2]
      const newpreOrderEle1 = preOrderEle.slice(1, newinOrderEle1.length + 1)
      const newpreOrderEle2 = preOrderEle.slice(newinOrderEle1.length + 1)
      preOrderArr = [...preOrderArr, newpreOrderEle1, newpreOrderEle2]
    }
  }
  return postOrder
}

console.log(findPostOrder(Array.from("EACBDGF"), Array.from("ABCDEFG")))
// [ "B", "D", "C", "A", "F", "G", "E" ]
推导中序序列

那是不可能的!

已知前序序列ABC,后序序列CBA,求中序序列。

这是一道多解题。

推荐结合nodemon在node环境下学习算法。

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

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

相关文章

  • 数据结构:叉树

    摘要:链式存储结构由于二叉树的每个结点最多有两个孩子,所以为每个结点设计一个数据域和两个指针域。最终能得到二叉树的完整结构。这篇文章主要介绍树结构中的一种特殊存在——二叉树。主要内容有: 二叉树的概念 二叉树的基本结构 二叉树的操作 概念 二叉树: 每个结点最多有两个子结点,两个子结点是有次序的,且子结点次序不能颠倒。两个子结点一般称之为左结点及右结点。 层次: 在树中,节点的层次从...

    Ashin 评论0 收藏0
  • 【数据结构初阶】第八篇——二叉树的链式结构(二叉树的前、中和后序遍历+层序遍历+链式结构的实现+相关

    摘要:代码实现如下二叉树的创建与销毁二叉树的创建问题通过前序遍历的数组给定一串字符串,代表的是空树,其他的都是节点。 ⭐️本篇博客我要来和大家一起聊一聊数据结构中的二...

    BigNerdCoding 评论0 收藏0

发表评论

0条评论

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