重建二叉树(前中遍历、后中遍历)

一个二叉树的遍历序列不能决定一棵二叉树,但某些不同的遍历序列组合可以唯一确定一棵二叉树。

给定一棵二叉树的前序遍历序列和中序遍历序列可以唯一确定一棵二叉树的结构,给定一棵二叉树的后序遍历序列和中序遍历序列也可以唯一确定一棵二叉树的结构。

  注意:这还有一个条件:二叉树的任意两个结点的值都不相同

 

一. 根据前序遍历序列和中序遍历序列重建二叉树

参考:剑指 Offer 07. 重建二叉树

原理:分治

  前序遍历性质: 节点按照 [ 根节点 | 左子树 | 右子树 ] 排序。
  中序遍历性质: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序。

根据以上性质,可得出以下推论:

  1. 前序遍历的 首元素 为树的根节点 node 的值。

  2. 在中序遍历中搜索根节点 node 的索引 ,可将 中序遍历 划分为 [ 左子树 | 根节点 | 右子树 ] 。

  3. 根据中序遍历中的左 / 右子树的节点数量,可将 前序遍历 划分为 [ 根节点 | 左子树 | 右子树 ] 。

  (确定左子树和右子树在中序数组和前序数组中的左右边界位置)

按照上述步骤可以分出二叉树的左右子树,而对于子树的左右子树,也可同上进行划分。

由此考虑通过递归对所有子树进行划分,从而重建二叉树。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int[] preorder;
    HashMap<Integer, Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        this.preorder = preorder;
        for(int i = 0; i < inorder.length; i++)
            map.put(inorder[i], i);
        return recur(0, preorder.length-1, 0, inorder.length-1);
    }
    TreeNode recur(int pS, int pE, int iS, int iE) {
        if(iE < iS || pE < pS)
            return null;                                        // 递归终止
        TreeNode node = new TreeNode(preorder[pS]);             // 建立根节点
        int i = map.get(preorder[pS]);                          // 划分中序数组中根节点、左子树、右子树
        node.left = recur(pS+1, pS+i-iS, iS, i-1);              // 左子树递归(由前、中序数组中左子树左右边界决定)
        node.right = recur(pS+i-iS+1, pE, i+1, iE);             // 右子树递归(由……右子树……)
        return node;                                            // 回溯返回根节点
    }
}

PS: 使用哈希表 map 存储中序遍历的值与索引的映射,查找操作的时间复杂度为 O(1)

时间复杂度O(n),空间复杂度O(n)

 

二、根据后序遍历序列和中序遍历序列重建二叉树

原理:分治

  后序遍历性质: 节点按照 [ 左子树 | 右子树 | 根节点 ] 排序。
  中序遍历性质: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序。

根据以上性质,可得出以下推论:

  1. 后序遍历的 尾元素 为树的根节点 node 的值。

  2. 在中序遍历中搜索根节点 node 的索引 ,可将 中序遍历 划分为 [ 左子树 | 根节点 | 右子树 ] 。

  3. 根据中序遍历中的左 / 右子树的节点数量,可将 后序遍历 划分为 [ 左子树 | 右子树 | 根节点 ] 。

  (确定左子树和右子树在中序数组和后序数组中的左右边界位置)

类似 前中遍历重建二叉树 代码如下:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int[] postorder;
    HashMap<Integer, Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        this.postorder = postorder;
        for(int i = 0; i < inorder.length; i++)
            map.put(inorder[i], i);
        return recur(0, postorder.length-1, 0, inorder.length-1);
    }
    TreeNode recur(int pS, int pE, int iS, int iE) {
        if(iE < iS || pE < pS)
            return null;                                        // 递归终止
        TreeNode node = new TreeNode(postorder[pE]);            // 建立根节点
        int i = map.get(postorder[pE]);                         // 划分中序数组中根节点、左子树、右子树
        node.left = recur(pS, pS + i - iS - 1, iS, i-1);        // 左子树递归(由后、中序数组中左子树左右边界决定)
        node.right = recur(pS + i - iS, pE - 1, i + 1, iE);     // 右子树递归(由……右子树……)
        return node;                                            // 回溯返回根节点
    }
}
posted @ 2021-02-19 14:52  zjcfrancis  阅读(279)  评论(0编辑  收藏  举报