#剑指 Offer 07. 重建二叉树

1.题目

剑指 Offer 07. 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:

3

/
9 20
/
15 7

限制:

0 <= 节点个数 <= 5000

2.自我思路与实现

本题未自我解决
思路:因为树的遍历是个递归过程,那么重构应该也是个递归过程,因此本题的方法应当是递归,且递归应当时创建子树的过程,但具体实现不明白

3.总结思路与实现

1.递归法
前序遍历的顺序是:根节点、左子树、右子树
中序遍历的顺序是:左子树、根节点、右子树
(后序遍历的顺序是:左子树、右子树、根节点)
在前序遍历中,根节点在第一个位置上,可以由此在中序遍历中找到根节点的位置
在中序遍历中,根节点左边全是左子树,右边全是右子树,由此可以计算出左子树的节点数量和右子树的节点数量,可得到左子树的前序、中序遍历范围和右子树的前序、中序遍历范围,可通过递归的方式重建左子树和右子树,从而重建二叉树
1.使用Map将中序遍历的元素和角标对应,以便从前序遍历中的根节点值快速得到其在中序遍历中的位置
2.调用递归方法, 对应前序和中序起始和结束角标都是0和length - 1
递归方法的基准情形有三个:
1.前序遍历的起始角标大于结束,当前树为空
2.前序遍历的起始角标等于结束,当前树只有一个节点,就是根节点
3.前序遍历的起始角标大于结束,当前树有多个节点,从前序遍历得到根的值,从中序遍历得到根的角标,进而得到左子树和右子树的范围,递归重建左右子树,最后链接到根上
时间n:遍历一遍中序,每个节点创建
空间n:递归存储整棵树,哈希表

/**
 * 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) {
        //特例判断
        if(preorder == null || preorder.length == 0)
            return null;

        //获得中序遍历的元素与角标存入哈希表
        Map<Integer, Integer> index = new HashMap<>();
        for(int i = 0; i < inorder.length; i++)
        {
            index.put(inorder[i], i);
        } 
       
       return buildTree(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1, index);
        

    }
    public TreeNode buildTree(int[] preorder, int preLeft, int preRight, 
                                int[] inorder, int inLeft, int inRight, 
                                Map<Integer, Integer> index)
    {
        //前序遍历开始大于结束,则树中无元素
        if(preLeft > preRight)
            return null;
        TreeNode root = new TreeNode(preorder[preLeft]);
        //前序遍历开始等于结束,树中只有一个元素
        if(preLeft == preRight)
            return root;
        else
        {
            int rootIndex = index.get(preorder[preLeft]);//根在中序遍历中的角标
            int leftNum = rootIndex - inLeft;//中序遍历中根的左边是左子树
            int rightNum = inRight - rootIndex; //中序遍历中根的右边是右子树
            TreeNode leftSubTree = buildTree(preorder, preLeft + 1, preLeft + leftNum, 
                                                inorder, rootIndex - leftNum, rootIndex - 1, 
                                                index); //左子树在前序和中序遍历之中的位置
            TreeNode rightSubTree = buildTree(preorder, preLeft + leftNum + 1, preLeft + leftNum + rightNum, 
                                                inorder, rootIndex + 1, rootIndex + rightNum, 
                                                index);//右子树在前序和中序遍历之中的位置
            root.left = leftSubTree;//链接左子树
            root.right = rightSubTree;//链接右子树
        }
        return root;
    }
}

2.迭代法
对于先序遍历中的两个相邻数mn(m在n前),有三种情况
一,n是m的左子树节点的值
二,n是m右子树节点的值(此时m无左子树)
三,n是m某个祖先节点的右节点(m没有子树)
反映到前序遍历 preorder = [3,9,20,15,7],从根出发,有左子节点就向左下,直到最左下角
中序遍历 inorder = [9,3,15,20,7],从最左下角向上,碰到有节点有右子节点,就转入该右子树,从左下角开始

1.创建栈,并将根节点压入栈中
2.开始遍历前序遍历,将栈顶端节点定为node,与最左下角节点值(中序遍历第一个元素)比较
相等:已经到达最左下角,向上找到存在有右子节点的节点,设为node的右子节点,循环2
不等:继续遍历前序数组,循环2

/**
 * 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) {
        //特例判断
        if(preorder == null || preorder.length == 0)
            return null;
        //建立根节点
        TreeNode root = new TreeNode(preorder[0]);
        //中序遍历指针
        int inIndex = 0;
        //创建栈并将根节点压入栈中
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        //遍历前序遍历
        for(int i = 1; i < preorder.length; i++)
        {
            //栈顶节点
            TreeNode node = stack.peek();
            

            if(node.val != inorder[inIndex])//不断创建左子树
            {
                node.left = new TreeNode(preorder[i]);
                stack.push(node.left);
            }
            else
            {   //向上找到存在右子节点的节点
                while(!stack.isEmpty() && stack.peek().val == inorder[inIndex])//栈不为空(保证存在根的左子节点)
                                                                            //中序向上遇到存在右子节点的节点会转向
                {
                    node = stack.pop();
                    inIndex++;
                }
                
                //从此节点开始重新寻找该节点的左子树
                node.right = new TreeNode(preorder[i]);
                stack.push(node.right);
            }
            
        }
        return root;
    }
}

4.相关知识

  • 树的相关一般选择递归
  • Stack的三个常用方法:push()压入,pop()弹出最顶端元素并返回,peek()返回最顶端元素
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值