Leetcode——重建二叉树(前序+中序)

1.题目

在这里插入图片描述

(1)递归

二叉树中不能有重复元素!
在这里插入图片描述

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return helper(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
    }

    public TreeNode helper(int[] preorder, int preLeft, int preRight, int[] inorder, int inLeft, int inRight) {
        // 递归终止条件
        if (inLeft > inRight || preLeft > preRight) 
            return null;

        // val 为前序遍历第一个的值,也即是根节点的值
        // idx 为根据根节点的值来找中序遍历的下标
        int idx = inLeft, val = preorder[preLeft];
        TreeNode root = new TreeNode(val);

        for (int i = inLeft; i <= inRight; i++) {
            if (inorder[i] == val) {
                idx = i;
                break;
            }
        }

        // 根据 idx 来递归找左右子树,始终保持左闭右开的区间原则

		//idx - inLeft:左子树长度
        root.left = helper(preorder, preLeft + 1, preLeft + (idx - inLeft), inorder, inLeft, idx - 1);
        root.right = helper(preorder, preLeft + (idx - inLeft) + 1, preRight, inorder, idx + 1, inRight);
        return root;
    }
}

(2)迭代

      3
    /   \
   9     7
  / \
 20  15

首先假设我们只有先序遍历的数组,如果还原一颗树,会遇到什么问题。

preorder = [3, 9, 20, 15, 7 ]

首先我们把 3 作为根节点,然后到了 9 ,就出现一个问题,9 是左子树还是右子树呢?
所以需要再加上中序遍历的数组来确定。

inorder = [ 20, 9, 15, 3, 7 ]

我们知道中序遍历,首先遍历左子树,然后是根节点,最后是右子树。这里第一个遍历的是 20 ,说明先序遍历的 9 一定是左子树,接下来的 20 同理,所以可以目前构建出来的树如下。

      3
    /   
   9    
  / 
 20  

同时,还注意到此时先序遍历的 20 和中序遍历 20 相等了,说明什么呢?
说明中序遍历的下一个数 15 不是左子树了,如果是左子树,那么中序遍历的第一个数就不会是 20

我们来假设几种情况,来想一下。

  • 如果是 3 的右子树, 20 和 9 的右子树为空,那么中序遍历就是20 9 3 15。、
  • 如果是 9 的右子树,20 的右子树为空,那么中序遍历就是20 9 15。
  • 如果是 20 的右子树,那么中序遍历就是20 15。

之前已经遍历的根节点是 3 9 20,把它倒过来,即20 9 3,然后和上边的三种中序遍历比较,会发现 15 就是最后一次相等的节点的右子树。

第 1 种情况,中序遍历是20 9 3 15,和20 9 3 都相等,所以 15 是3 的右子树。
第 2 种情况,中序遍历是20 9 15,只有20 9 相等,所以 15 是 9 的右子树。
第 3 种情况,中序遍历就是20 15,只有20 相等,所以 20 是 15 的右子树。
而此时我们的中序遍历数组是inorder = [ 20, 9 ,15, 3, 7 ],20 匹配,9匹配,最后一次匹配是 9,所以 15 是 9的右子树。

     3
    /   
   9    
  / \
 20  15

综上所述,我们用一个栈保存已经遍历过的节点,遍历前序遍历的数组,一直作为当前根节点的左子树,直到当前节点和中序遍历的数组的节点相等了,那么我们正序遍历中序遍历的数组,倒着遍历已经遍历过的根节点(用栈的 pop 实现),找到最后一次相等的位置,把它作为该节点的右子树。

上边的分析就是迭代总体的思想,代码的话还有一些细节注意一下。用一个栈保存已经遍历的节点,用 curRoot 保存当前正在遍历的节点。

public TreeNode buildTree(int[] preorder, int[] inorder) {
    if (preorder.length == 0) {
        return null;
    }
    Stack<TreeNode> roots = new Stack<TreeNode>();
    int pre = 0;
    int in = 0;
    //先序遍历第一个值作为根节点
    TreeNode curRoot = new TreeNode(preorder[pre]);
    TreeNode root = curRoot;
    roots.push(curRoot);
    pre++;
    //遍历前序遍历的数组
    while (pre < preorder.length) {
        //出现了当前节点的值和中序遍历数组的值相等,寻找是谁的右子树
        if (curRoot.val == inorder[in]) {
            //每次进行出栈,实现倒着遍历
            while (!roots.isEmpty() && roots.peek().val == inorder[in]) {
                curRoot = roots.peek();
                roots.pop();
                in++;
            }
            //设为当前的右孩子
            curRoot.right = new TreeNode(preorder[pre]);
            //更新 curRoot
            curRoot = curRoot.right;
            roots.push(curRoot);
            pre++;
        } else {
            //否则的话就一直作为左子树
            curRoot.left = new TreeNode(preorder[pre]);
            curRoot = curRoot.left;
            roots.push(curRoot);
            pre++;
        }
    }
    return root;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yawn__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值