zoukankan      html  css  js  c++  java
  • 重建二叉树

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

    例如,给出

    前序遍历 preorder = [3,9,20,15,7]
    中序遍历 inorder = [9,3,15,20,7]

    返回如下的二叉树:

        3
       / 
      9  20
        /  
       15   7

    限制:

    0 <= 节点个数 <= 5000

    题解1:分治法(递归思想)

      思路:

      前序遍历的第 1 个结点一定是二叉树的根结点;
      在中序遍历中,根结点把中序遍历序列分成了两个部分,左边部分构成了二叉树的根结点的左子树,右边部分构成了二叉树的根结点的右子树。
      查找根结点在中序遍历序列中的位置,可以遍历,也可以在一开始就记录下来。

     1 import java.util.HashMap;
     2 import java.util.Map;
     3 
     4 class TreeNode {
     5     int val;
     6     TreeNode left;
     7     TreeNode right;
     8 
     9     TreeNode(int x) {
    10         val = x;
    11     }
    12 }
    13 
    14 
    15 public class Solution {
    16 
    17     // 使用全局变量是为了让递归方法少传一些参数,不一定非要这么做
    18 
    19     private Map<Integer, Integer> reverses;
    20     private int[] preorder;
    21 
    22     public TreeNode buildTree(int[] preorder, int[] inorder) {
    23         int preLen = preorder.length;
    24         int inLen = inorder.length;
    25 
    26         // 可以不做判断,因为题目中给出的数据都是有效的
    27         if (preLen != inLen) {
    28             return null;
    29         }
    30 
    31         this.preorder = preorder;
    32 
    33         // 以空间换时间,否则,找根结点在中序遍历中的位置需要遍历
    34         reverses = new HashMap<>(inLen);
    35         for (int i = 0; i < inLen; i++) {
    36             reverses.put(inorder[i], i);
    37         }
    38 
    39         return buildTree(0, preLen - 1, 0, inLen - 1);
    40     }
    41 
    42     /**
    43      * 根据前序遍历数组的 [preL, preR] 和 中序遍历数组的 [inL, inR] 重新组建二叉树
    44      *
    45      * @param preL 前序遍历数组的区间左端点
    46      * @param preR 前序遍历数组的区间右端点
    47      * @param inL  中序遍历数组的区间左端点
    48      * @param inR  中序遍历数组的区间右端点
    49      * @return 构建的新二叉树的根结点
    50      */
    51     private TreeNode buildTree(int preL, int preR,
    52                                int inL, int inR) {
    53         if (preL > preR || inL > inR) {
    54             return null;
    55         }
    56         // 构建的新二叉树的根结点一定是前序遍历数组的第 1 个元素
    57         int pivot = preorder[preL];
    58         TreeNode root = new TreeNode(pivot);
    59 
    60         int pivotIndex = reverses.get(pivot);
    61 
    62         // 这一步得画草稿,计算边界的取值,必要时需要解方程,并不难
    63         root.left = buildTree(preL + 1, preL + (pivotIndex - inL), inL, pivotIndex - 1);
    64         root.right = buildTree(preL + (pivotIndex - inL) + 1, preR, pivotIndex + 1, inR);
    65         return root;
    66     }
    67 }

    题解2:

      递归解析:
      递推参数: 前序遍历中根节点的索引pre_root、中序遍历左边界in_left、中序遍历右边界in_right。
      终止条件: 当 in_left > in_right ,子树中序遍历为空,说明已经越过叶子节点,此时返回 nullnull 。

      递推工作:
      1.建立根节点root: 值为前序遍历中索引为pre_root的节点值。
      2.搜索根节点root在中序遍历的索引i: 为了提升搜索效率,本题解使用哈希表 dic 预存储中序遍历的值与索引的映射关系,每次搜索的时间复杂度为 O(1)O(1)。
      3.构建根节点root的左子树和右子树: 通过调用 recur() 方法开启下一层递归。
        左子树: 根节点索引为 pre_root + 1 ,中序遍历的左右边界分别为 in_left 和 i - 1。
        右子树: 根节点索引为 i - in_left + pre_root + 1(即:根节点索引 + 左子树长度 + 1),中序遍历的左右边界分别为 i + 1 和 in_right。
      4.返回值: 返回 root,含义是当前递归层级建立的根节点 root 为上一递归层级的根节点的左或右子节点。

     1 class Solution {
     2     HashMap<Integer, Integer> dic = new HashMap<>();
     3     int[] po;
     4     public TreeNode buildTree(int[] preorder, int[] inorder) {
     5         po = preorder;
     6         for(int i = 0; i < inorder.length; i++) 
     7             dic.put(inorder[i], i);
     8         return recur(0, 0, inorder.length - 1);
     9     }
    10     TreeNode recur(int pre_root, int in_left, int in_right) {
    11         if(in_left > in_right) return null;
    12         TreeNode root = new TreeNode(po[pre_root]);
    13         int i = dic.get(po[pre_root]);
    14         root.left = recur(pre_root + 1, in_left, i - 1);
    15         root.right = recur(pre_root + i - in_left + 1, i + 1, in_right);
    16         return root;
    17     }
    18 }

    题解3:

     1 class Solution {
     2     public TreeNode buildTree(int[] preorder, int[] inorder) {
     3         return buildTree(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
     4     }
     5     
     6     /**
     7      * preStart - preEnd 代表了在 preorder[] 中查找的区间范围
     8      * inStart - inEnd   代表了在 inorder[]  中查找的区间范围
     9      */
    10     private TreeNode buildTree(int[] preorder, int preStart, int preEnd
    11                             , int[] inorder, int inStart, int inEnd) {
    12         // 显而易见的结束条件
    13         if (preStart > preEnd || inStart > inEnd) {
    14             return null;
    15         }
    16         // 前序遍历的第一个节点为 root
    17         TreeNode root = new TreeNode(preorder[preStart]);
    18         
    19         // 在中序遍历的查找区间中找到 root 所处位置
    20         for (int i = inStart; i <= inEnd; i++) {
    21 
    22             if (inorder[i] == preorder[preStart]) {
    23                 // 前序区间的 start 从根节点后一位开始
    24                 // 前序区间的 end 是中序遍历确定的左子树节点个数 + 原先的preStart
    25                 // 中序区间的 start 不变,因为从头开始找左子树
    26                 // 中序区间的 end 最多达到当前根节点的前一位
    27                 root.left = buildTree(preorder, preStart + 1, i - inStart + preStart
    28                                     , inorder, inStart, i - 1);
    29 
    30                 // 前序区间的 start 从当前根节点跳过左子树个数开始
    31                 // 前序区间的 end 保持不变,扫描到底
    32                 // 中序区间的 start 从根节点的后一位开始
    33                 // 中序区间的 end 保持不变
    34                 root.right = buildTree(preorder, i - inStart + preStart + 1, preEnd
    35                                     , inorder, i + 1, inEnd);
    36             }
    37         }
    38         return root;
    39     }
    40 }

    题解4:

    使用Arrays.copyOfRange()方法

     1 class Solution {
     2     public TreeNode buildTree(int[] preorder, int[] inorder) {
     3         int n = preorder.length;
     4         if (n == 0)
     5             return null;
     6         int rootVal = preorder[0], rootIndex = 0;
     7         for (int i = 0; i < n; i++) {
     8             if (inorder[i] == rootVal) {
     9                 rootIndex = i;
    10                 break;
    11             }
    12         }
    13         TreeNode root = new TreeNode(rootVal);
    14         
    15         root.left = buildTree(Arrays.copyOfRange(preorder, 1, 1 + rootIndex),Arrays.copyOfRange(inorder, 0, rootIndex));
    16 
    17         root.right = buildTree(Arrays.copyOfRange(preorder, 1 + rootIndex, n),Arrays.copyOfRange(inorder, rootIndex + 1, n));
    18 
    19         return root;
    20     }
    21 }

    来源:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/

  • 相关阅读:
    4.使用webpack-dev-server工具实现自动打包编译的功能
    3.使用webpack配置文件webpack.confg.js配置打包文件的入口和出口
    2.webpack最基本的使用方式
    启动nginx 80端口被占用:tcp 0 0 127.0.0.1:80 127.0.0.1:34932 TIME_WAIT
    template标签就相当于React中的fragment
    初窥AST
    npm 中的 --
    你不知道的环境变量(转)
    非捕获分组 正向预查 与反向预查
    border-radius 如何计算
  • 原文地址:https://www.cnblogs.com/treasury/p/12595620.html
Copyright © 2011-2022 走看看