前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【一天一大 lee】从中序与后序遍历序列构造二叉树 (难度:中等)-Day20200925

【一天一大 lee】从中序与后序遍历序列构造二叉树 (难度:中等)-Day20200925

作者头像
前端小书童
发布2020-09-29 20:19:49
5630
发布2020-09-29 20:19:49
举报
文章被收录于专栏:前端小书童前端小书童

题目:[1]

根据一棵树的中序遍历与后序遍历构造二叉树。

注意:你可以假设树中没有重复的元素。

例如,给出:

中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]

返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

抛砖引玉

思路

参数:

  • 中序遍历的数组
  • 后续遍历的数组

思路

  • 借助后续遍历的节点找到每层子树的根节点
  • rootIndex:二叉子树在后续遍历数组中的位置索引
  • leftIndex:二叉子树的左子树在后续遍历数组中的位置索引
  • rightIndex:二叉子树的右子树在后续遍历数组中的位置索引

查找根节点,及当前根节点左右子节点的逻辑:

  1. 后序遍历数组倒序遍历依次作为根节点
  2. 当前根节点的的左右子节点从中序遍历数组中查找:
    • 这个节点的左节点在中序遍历数组中这个元素的前一位
    • 这个节点的右节点在中序遍历数组中这个元素的后一位

深度优先搜索(DFS)

递归参数:

  • 左子树根节点在 postorder 中的索引
  • 右子树根节点在 postorder 中的索引

终止条件:

因为后续遍历是先遍历左子树再遍历右子树最后遍历根节点, 那么右子树的索引一定大于左子树的索引,当不满足是说明节点遍历完成,终止递归

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {number[]} inorder  中序遍历
 * @param {number[]} postorder 后序遍历
 * @return {TreeNode}
 */
var buildTree = function(inorder, postorder) {
  let rootIndex = postorder.length - 1,
    map = new Map()
  // 记录在中序遍历数组中每个节点的位置,方便在得到根节点时查找左右子树节点
  for (let i = 0; i < inorder.length; i++) {
    map.set(inorder[i], i)
  }
  function dfs(leftIndex, rightIndex) {
    if (leftIndex > rightIndex) return null
    let root = new TreeNode(postorder[rootIndex]),
      i = map.get(postorder[rootIndex])
    rootIndex--
    root.right = dfs(i + 1, rightIndex)
    root.left = dfs(leftIndex, i - 1)
    return root
  }
  return dfs(0, inorder.length - 1)
}

迭代

倒序遍历中序遍历数组(inorder):

  • 二叉树的遍历逻辑变成了:先遍历右孩子,再遍历根节点,最后遍历左孩子

倒序遍历后序遍历数组(postorder):

  • 二叉树的遍历逻辑变成了:先遍历根节点,再遍历右孩子,最后遍历左孩子

那么,倒序遍历后序遍历数组数组时,两个相邻的节点[a,b],两个节点在二叉树中的关系:

  1. b 是 a 的右子树上的节点
  2. a 没有右子树,并且 b 是与 a 子树相连的的左子树上的节点

实现

  • postorder 中最后一个元素是整个二叉树的根节点
  • 倒序遍历 postorder,inorder:
  • inorder 中先遇到右子树上的节点:
    • 不等于上一个生成子树的节点,则说明是上一个子树的右节点即情况 1,生成子树追击到上一个子树的 right 上
    • 等于上一个生成子树的节点,则说明在 inorder 中遍历到了根节点,即情况 2,那么 inorder 继续遍历先遇到的应该是这个根节点对应子树的左节点
var buildTree = function(inorder, postorder) {
  let rootIndex = postorder.length - 1,
    root = new TreeNode(postorder[rootIndex]),
    // 维护下一次遍历追击子树的最新子树
    queue = [root],
    inorderIndex = inorder.length - 1

  rootIndex--

  while (rootIndex >= 0) {
    let node = queue[queue.length - 1],
      nodeVal = postorder[rootIndex]
    // 不等于上一个生成子树的节点,则说明是上一个子树的右节点
    if (node.val !== inorder[inorderIndex]) {
      node.right = new TreeNode(nodeVal)
      queue.push(node.right)
    } else {
      // 维护inorder的倒序遍历索引跳过已经生成树的根节点
      while (
        queue.length &&
        queue[queue.length - 1].val === inorder[inorderIndex]
      ) {
        node = queue.pop()
        inorderIndex--
      }
      // 情况 2
      node.left = new TreeNode(nodeVal)
      queue.push(node.left)
    }

    rootIndex--
  }

  return root
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端小书童 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 抛砖引玉
    • 深度优先搜索(DFS)
      • 迭代
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档