LeetCode 105 从先序和中序遍历构建二叉树

这道题目在 《剑指Offer》一书中有提到过,也有解析的过程,不过时间隔得久,大概只有潜意识的印象了。

初看这道题有点让人摸不着头脑,虽然一直强调树的算法一般是递归的算法,就算是递归算法,也无从下手

那就先举个例子先画几个图看看情况, 以这棵树为例

我们先把前序遍历和中序遍历列在一起, 然后将对应关系用箭头连接起来

首先是 红色的箭头, 前序遍历的第0个元素对应中序遍历的第1个元素

然后我们来观察观察这个3和这个树的关系, 中序遍历给我们直观的感受, 就是将树分成了左右两棵子树

上图中左右两个部分正好就是左子树和右子树, 是不是有点头绪了?

接下来我们再来分别考察左右两个子树

左子树:

我们已经知道 9 属于 3 的左子树, 而 9 刚好是 第0个元素, 它的右边就是他的父节点3, 所以只剩下孤零零的 9, 也就是说, 9 是 3 的左叶子

右子树:

前面我们知道 15 20 7 组成了 3 的右子树, 那它们又是怎么排列的呢?

3,9 我们用过了, 现在我们来看20, 20 刚好位于15 和 7 的中间, 再来看看我们的树, 20正好是 15 和 7 的父节点, 这个不可能又是巧合吧?

如果我们把一开始的例子换成是这个, 有没有发现同样的分析过程也适用.

说到底还是个递归问题呀

这种方式的缺点是, 递归太多次了, 每次一个元素不在对应的子树范围中,就尝试下一个元素, 这样会比较浪费

针对这种情况,我们可以改一下内部的递归函数,同时限制先序遍历的范围

这样可以减少遍历的次数,不过计算的边界的时候要小心, 最好多举一些例子去研究他们的关系

实际上这里 preTo 没什么作用, 应该可以直接去掉

其实递归的题目做多了就会有一种感觉, 要培养自己递归的思维方式.

 /**
    * inFrom < inTo 会出现在 [4,1,2,3], [1,2,3,4] 这个用例里面
    * 因为有 index - 1 这种操作
    */

  def buildTree(preorder: Array[Int], inorder: Array[Int]): TreeNode = {

    val len = preorder.length

    def buildByInorder(preIndex: Int, inFrom: Int, inTo: Int): TreeNode = {
      if (preIndex >= len || inFrom > inTo) null //只要是有减法, 就有这种边界的情况要注意
      else if (inFrom == inTo) new TreeNode(inorder(inFrom))
      else {
        val preorderValue = preorder(preIndex) //根据当前的下标, 用先序遍历的值, 去查询中序遍历的位置
        inorder.indexOf(preorderValue, inFrom) match {
          case index if index < inFrom || index > inTo => //index 超过了子树的范围, 表示当前值在另一个子树中
            buildByInorder(preIndex + 1, inFrom, inTo)    //使用下一个元素构建尝试匹配相同的范围
          case index =>
            val node = new TreeNode(inorder(index))
            //左子树的元素是 中序遍历 的 从 inFrom 到 index - 1 组成的元素
            val left = buildByInorder(preIndex + 1, inFrom, index - 1)
            //右子树的元素是 中序遍历 的 从 index + 1 到 inTo 组成的元素
            val right = buildByInorder(preIndex + 1, index + 1, inTo)
            node.left = left
            node.right = right
            node
        }
      }
    }

    if (preorder.length == inorder.length && preorder.nonEmpty) {
      buildByInorder(0, 0, len - 1)
    } else null

  }

  def buildTree1(preorder: Array[Int], inorder: Array[Int]): TreeNode = {

    val len = preorder.length

    def buildByInorder1(preFrom: Int, preTo: Int, inFrom: Int, inTo: Int): TreeNode = {
      if (inFrom >= len || preFrom >= len || preTo < preFrom) {
        null
      } else if (inFrom == inTo) {
        new TreeNode(inorder(inFrom))
      } else {
        val preHead = preorder(preFrom)
        inorder.indexOf(preHead, inFrom) - inFrom match {//注意这里要减去 inFrom, 表示是范围内的第几个元素
          case -1 => null                                //如果不在范围内 或者 超出范围, 则返回 null
          case index if index > inTo => null
          case index =>
            val node = new TreeNode(preHead)
            val leftInFrom = inFrom
            val leftInTo = inFrom + index - 1
            val rightInFrom = inFrom + index + 1
            val rightInTo = inTo

            val left = buildByInorder1(preFrom + 1, preFrom + index, leftInFrom, leftInTo) //分别构建左子树
            val right = buildByInorder1(preFrom + index + 1, preTo, rightInFrom, rightInTo) //分别构建右子树
            node.left = left
            node.right = right
            node

        }
      }

    }

    if (preorder.length == inorder.length && preorder.nonEmpty) buildByInorder1(0, len - 1, 0, len - 1)
    else null

  }

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值