【剑指offer】07. 重建二叉树

267 阅读3分钟

题目描述

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

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

题解


// 时间复杂度: 2 ms , 在所有 Java 提交中击败了 97.96% 的用户
// 空间复杂度:38.5 MB, 在所有 Java 提交中击败了 94.78% 的用户
class Solution {
	// 初始化一个hashmap
	private Map<Integer, Integer> indexForInOrders = new HashMap<>();

	public TreeNode buildTree(int[] preorder, int[] inorder) {
		// for循环遍历中序表,将中序表的值(key)和索引(value)存入hashmap
		for (int i = 0; i < inorder.length; i++)
			indexForInOrders.put(inorder[i], i);
		// 调用递归函数,传入:
		// 前序表,前序表左索引0,前序表右索引length - 1,中序表左索引0
		return buildTree(preorder, 0, preorder.length - 1, 0);
	}

	// 定义递归函数
	private TreeNode buildTree(int[] pre, int preL, int preR, int inL) {
		// 若前序表左索引大于有索引,返回null
		if (preL > preR)
			return null;
		// 取前序表左索引遍历值作为节点值
		TreeNode root = new TreeNode(pre[preL]);
		// 找到节点值在中序表的对应相同值(在中序表的)索引
		int inIndex = indexForInOrders.get(root.val);
		// 取节点值在中序表的索引,减去节点值在前序表的索引,得到左子树的
		// 规模(左子树节点数)
		int leftTreeSize = inIndex - inL;
		// 构建左子树,调用递归函数,传入:
		// 前序表,前序表左索引右移,新前序表右索引=前序表左索引+左子树规模
		// 中序表左索引
		root.left = buildTree(pre, preL + 1, preL + leftTreeSize, inL);
		// 构建右子树,调用递归函数,传入:
		// 新前序表左索引=前序表左索引+左子树规模+1(因为要到右子树位置遍历),
		// 前序表右索引,新中序表左索引=中序表左索引+左子树规模+1
		root.right = buildTree(pre, preL + leftTreeSize + 1, preR, inL + leftTreeSize + 1);
		// 返回树
		return root;
	}
}
// 时间复杂度: 13 ms , 在所有 Java 提交中击败了 20.86% 的用户
// 空间复杂度:38.6 MB , 在所有 Java 提交中击败了 92.52% 的用户
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder == null || inorder == null || preorder.length <= 0 || inorder.length <= 0) {
            return null;
        }
		// 调用递归方法
        TreeNode root = reConstruct(preorder, 0, preorder.length-1, inorder, 0, inorder.length-1);
        return root;
    }

	// 定义递归方法:
	// 初始传入:前序结果表,前序表遍历头索引,前序表遍历尾索引
	// 中序结果表,中序表遍历头索引,中序表遍历尾索引
    public TreeNode reConstruct(int[] pre, int startpre, int endpre, int[] in, int startin, int endin) {
        // 如果中序表头索引大于中序表尾索引,
		// 或前序表前索引大于前序表尾索引,填null
		if (startin > endin || startpre > endpre) {
            return null;
        }
		// 使用前序表头索引遍历数值,赋值结点数值
		//(前序表第一个数为根节点)
        TreeNode root = new TreeNode(pre[startpre]); // startpre遍历前序遍历结果表pre
        // 每调用一次递归方法,就对中序表做一次for循环遍历,
		// 如果找出前序表头索引遍历数,与中序表遍历数的相同对应数in[i]
		// 则再次调用递归方法
		// 左子树构建时,中序表头索引保持不变,中序表尾索引每递归一次则左移一次
		// 缩小for循环搜索范围
		// 右子树构建时,中序表尾索引保持不变,中序表头索引每递归一次则右移一次,
		// 缩小for循环搜索范围
		for (int i = startin; i <= endin; i++) {
            // 若前序表头索引数等于中序表当前遍历元素,执行:
			// (从前序表第一个数(根节点),可以找到中序表的根节点)
			// (之后,前序表头索引将遍历到左子树元素,可以找到
			// 中序表中的左子树元素)
			if (pre[startpre] == in[i]) {
				// System.out.print("startpre: ");System.out.print(startpre);
                // System.out.print(" endpre: ");System.out.print(endpre);
                // System.out.print(" startin: ");System.out.print(startin);
                // System.out.print(" endin: ");System.out.println(endin);
				
				// 左子树开始递归,构建左子树,传入:
				// 1.前序表,2.前序表头索引右移一位,
				// 3.新前序表尾索引 = 前序表尾索引-中序表尾索引+i,
				// (i为上一次找到的前序表和中序表的相同对应数索引,
				// 新前序表尾索引第一次会被赋值为根节点在中序表的位置。
				// 之后直到左子树构建完,新前序表尾索引的值都不会变)
				// 4.中序表,5.中序表头索引,
				// 6.新中序表尾索引 = i-1(左子树,原i位置左移)
                root.left = reConstruct(pre, startpre+1, endpre-endin+i, in, startin, i-1);
                // 左子树构建完
				// 右子树开始递归,传入:
				// 1.前序表,2.新前序表头索引=前序表头索引-中序表头索引+i+1
				// 3.前序表尾索引,
				// 4.中序表,5.新中序表头索引为i+1(右子树,原i位置右移)
				// 6.中序表尾索引
				root.right = reConstruct(pre, startpre+i-startin+1, endpre, in, i+1, endin);
            }
        }
        return root;
    }
}