LeetCode 105.从先序和中序遍历序列构造二叉树

147 阅读2分钟

「这是我参与2022首次更文挑战的第37天,活动详情查看:2022首次更文挑战」。

题目:给定一颗二叉树的先序和中序遍历结果,要求根据此结果恢复二叉树,并且返回二叉树的根节点。

解题思路

给定一颗二叉树的先序序列preOrder和中序序列inOrder,我们可以直接恢复成一颗二叉树,假如preOrder=[3,9,20,15,7]inOrder=[9,3,15,20,7],恢复过程如下:

根据先序序列,首先知道3为这棵树的根节点,之后在中序序列中找到3,则3的左边为根节点的左子树,3的右边为根节点的右子树。因为中序序列3的左边只有9,因此确定93的左子树。之后还剩20、15、7三个节点,根据先序序列可知20为右子树的根节点,而157由中序序列可知分别为20的左子树和右子树。因此恢复的二叉树就如下:

1645068549771.png

上述过程实际上就是一个递归,对于先序序列preOrder和中序序列inOrder,可得下面的示意图:

1645068830931.png

首先在inOrder中确定根节点的索引位置,则inOrderindex左边即为左子树,右边即为右子树。如果直接通过遍历获取根节点的索引,时间复杂度会增大,因此我们可以事先将inOrder的数据和对应索引存入map中,之后在map中查询即可,又因为inOrder左子树的长度和preOrder的左子树长度是相同的。因此可以确定preOrder的边界的索引,如下图:

1645069429095.png

根据上面索引,可得如下代码:

public TreeNode buildTree(int[] preorder, int[] inorder) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int i=0;i<inorder.length;i++){
            map.put(inorder[i], i);
        }
        return buildTree(preorder, 0, preorder.length-1, 0, inorder.length-1, map);

    }

    private TreeNode buildTree(int[] preorder, int preleft, int preright, int inleft, int inright, HashMap<Integer, Integer> map) {
        if(preleft>preright||inleft>inright) return null;
        int value = preorder[preleft];
        TreeNode root = new TreeNode(value);
        Integer index = map.get(value);
        root.left = buildTree(preorder, preleft+1, index-inleft+preleft, inleft, index-1, map);
        root.right = buildTree(preorder, index-inleft+preleft+1, preright, index+1, inright, map);
        return root;
    }

上述代码的时间复杂度和空间复杂度都为0(n)0(n)