4.17重建二叉树 - WisperDin/blog GitHub Wiki

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

思路:

递归实现,从根结点开始,逐步构建左右分支,直至构建出整个树

PreIndex :: 0,1,2,3,4,5,6,7
Pre      :: 1,2,4,7,3,5,6,8  (L:1~3 R:4~7)
Pre      :: 1,[2,4,7],[3,5,6,8]  (L:1~3 R:4~7)

InIndex  :: 0,1,2,3,4,5,6,7
In       :: 4,7,2,1,5,3,8,6 (L:0~2 R:4~7)
In       :: [4,7,2],1,[5,3,8,6] (L:0~2 R:4~7)

回忆用手用笔做的过程:

  1. 从前序表达式中找第一个元素,知道为根节点

  2. 在中序表达式中搜索这个根节点所在位置, 中序表达式中 根节点往左为左子树,根节点往右为右子树,同样,在 前序表达式 中 划分左子树和右子树 的对应位置

  3. 对左子树的前序与中序式子,右子树的前序与中序式子,递归进行1,2步骤

其中

转化为编程实现

  1. 步骤1没啥问题,就拿前序表达式中第一个元素作为根节点,步骤二在中序表达式中搜索这个根节点位置也不难,就是一个遍历搜索

  2. 然后,划分中序的左子树与右子树也不难,左子树::(开始根结点位置-1) 右子树::(根节点位置+1结束)

  3. 难点 是要在前序中确定左右子树的划分, 以前手做的时候的确可以一眼看出前序中的左子树,但是到计算机实现的时候就有点迷糊了,, 于是细化 这个步骤:

    • 首先分析左子树,由于前序的顺序是 中左右 ,所以左子树的开始位置肯定在 根节点的向后一个位置,
    • 左子树结束位置的判定,来源于从中序得来的左子树元素个数,然后进行偏移得到左子树结束位置
    • 前序左子树 表达式 (根节点位置+1~根节点位置+左子树元素个数)
    • 接着分析右子树,右子树的开始位置,是从左前序开始位置偏移左子树元素个数得到
    • 右子树的结束位置就是前序末尾
    • 前序右子树 表达式 (左前序位置偏移左子树元素个数~末尾)

实现(某位大佬的代码,看了好久之后理解了,加了注释,中间变量)

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
	public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
		TreeNode root = reConstructBinaryTree(pre, 0, pre.length - 1, in, 0, in.length - 1);
		return root;
	}
	//前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
	private TreeNode reConstructBinaryTree(int[] pre, int startPre, int endPre, int[] in, int startIn, int endIn) {

		if (startPre>endPre || startIn>endIn)
			return null;
		TreeNode root = new TreeNode(pre[startPre]);

		for (int i = startIn; i <= endIn; i++)
			if (in[i] == pre[startPre]) {
                // i-startIn 表示通过中序根节点位置 往左 得知的的 !!左子树元素个数
                //left----------
                int leftPreStart = startPre + 1;//开始位置=前序根节点位置+1
                int leftPreEnd = startPre + i - startIn; //末尾位置=(开始位置-1)+左子树元素个数
                //
                int leftInStart = startIn;//左子树的中序开始位置
                int leftInEnd = i - 1; //左子树的中序结束位置 中序中根节点位置的前一个
                //right----------
                int rightPreStart =  i - startIn + startPre + 1;//右前序开始位置=从左前序开始位置 偏移 左子树元素个数
                int rightPreEnd = endPre;//前序末尾
                //
                int rightInStart = i + 1 ; // 右子树的中序开始位置
                int rightInEnd = endIn; //中序末尾
                
                
				root.left = reConstructBinaryTree(pre, leftPreStart, leftPreEnd, in, leftInStart, leftInEnd);
				root.right = reConstructBinaryTree(pre, rightPreStart, rightPreEnd, in, rightInStart, rightInEnd);
				break;
			}

		return root;
	}
}