题目中有说 “可以假设树中没有重复的元素”,隐含的意思就是在前序和中序遍历的数组中如果存在某一个元素相等,即可判定为同一节点所对应的。
然后就是寻找规律,前序遍为根左右
,因此在前序遍历数组 [3, 9, 20, 15 , 7]
中,第一个元素「3」
即为最原始的根节点,且对应中序遍历数组 [9, 3, 15, 20, 7]
中的第个元素 inorder[1]
,而中序遍历为 左根右
,因此,在 inorder[1]
左边的即为 rootNode(3)
的左子树,在 inorder[1]
右边的即为 rootNode(3)
的右子树。
然后根据这个规律,就可以构造出树来。
1.递归实现
public TreeNode buildTree(int[] preorder, int[] inorder) {
if (preorder==null||preorder.length==0||inorder==null||inorder.length==0) return null;
return build(preorder, 0, preorder.length - 1,
inorder, 0, inorder.length - 1);
}
public TreeNode build(int[] preorder, int preStart, int preEnd,
int[] inorder, int inorStart, int inorEnd) {
//终止递归的条件,即子树不存在
if (preStart>preEnd) return null;
//构建当前子树的根节点,即前序遍历数组中的第一个元素
TreeNode root = new TreeNode(preorder[preStart]);
int rootIndexInInor = inorStart;
//求出在中序遍历中的根节点
for (; rootIndexInInor <= inorEnd;rootIndexInInor++)
if (preorder[preStart] == inorder[rootIndexInInor]) break;
int leftSubCounts = rootIndexInInor - inorStart;//计算出下一级左子树节点的个数
//构建下一级左子树
root.left = build(preorder, preStart + 1, preStart + leftSubCounts,
inorder, rootIndexInInor - leftSubCounts, rootIndexInInor - 1);
//构建下一级右子树
root.right = build(preorder, preStart + leftSubCounts + 1, preEnd,
inorder, rootIndexInInor + 1, inorEnd);
return root;
}
2.迭代实现
迭代实现的话,实际上就是完全模拟递归的构建过程,在这里借助了一个结构体来存储相关信息。
// 存储子树信息的结构体
static class Bean {
boolean isLeft;//用于判断处于父节点的哪边
int parentVal;//记录其父节点
int preStart, preEnd;//该子树在前序数组中的范围
int inorStart, inorEnd;//该子树在中序数组中的范围
}
一段中序数组,就对应着一个子树,利用相应的前序数组,找到其子树范围内根节点,将其根节点与上一级父节点连接即可,且拆分出左右子树,并将子树的信息存放在结构体中。
因此,以结构体单独来看,其对应的一个子树,也对应着一段中序序列,因此可以不断迭代,将中序数组不断拆分,最终构建出完整的树。
public TreeNode buildTree2(int[] preorder, int[] inorder) {
if (preorder==null||preorder.length==0
||inorder==null||inorder.length==0)
return null;
//临时存放节点的
HashMap<Integer, TreeNode> tmpNodes = new HashMap<>();
LinkedList<Bean> tmpBeans = new LinkedList<>();
TreeNode root = new TreeNode(preorder[0]);
tmpNodes.put(root.val, root);
int rootIndexInInor = 0;
for (;rootIndexInInor<inorder.length;rootIndexInInor++) {
if (root.val == inorder[rootIndexInInor]) break;
}
int leftSubCounts = rootIndexInInor;//计算出下一级左子树节点的个数
// 如果有左子树的话
if (leftSubCounts>0) {
Bean leftSubTree = new Bean();
leftSubTree.parentVal = root.val;
leftSubTree.isLeft = true;
leftSubTree.preStart = 1;
leftSubTree.preEnd = 1 + leftSubCounts - 1;
leftSubTree.inorStart = 0;
leftSubTree.inorEnd = rootIndexInInor - 1;
tmpBeans.add(leftSubTree);
}
// 如果有右子树的话
// rootIndexInInor+1 是因为 rootIndexInInor 是根节点的下标,
// 右移一位才是其右子树范围内的下边
if (rootIndexInInor+1<inorder.length) {
Bean rightSubTree = new Bean();
rightSubTree.parentVal = root.val;
rightSubTree.isLeft = false;
rightSubTree.preStart = leftSubCounts + 1;
rightSubTree.preEnd = preorder.length - 1;
rightSubTree.inorStart = rootIndexInInor + 1;
rightSubTree.inorEnd = inorder.length - 1;
tmpBeans.add(rightSubTree);
}
while (!tmpBeans.isEmpty()) {
Bean tmpBean = tmpBeans.poll();
TreeNode tmpRoot = new TreeNode(preorder[tmpBean.preStart]);
tmpNodes.put(tmpRoot.val, tmpRoot);
// 跟父节点联系起来
TreeNode parent = tmpNodes.get(tmpBean.parentVal);
if (tmpBean.isLeft) parent.left = tmpRoot;
else parent.right = tmpRoot;
// 当前树只有一个节点的时候,即叶子节点
if (tmpBean.inorEnd==tmpBean.inorStart) continue;
int i = tmpBean.inorStart;
for (; i <= tmpBean.inorEnd; i++) {
// 根据当前节点值在中序遍历数组中找到对应的下标
if (tmpRoot.val == inorder[i])
break;
}
// 如果当前节点还存在左子树,构建左子树对应的结构体
int tmpSubLeftCounts = i - tmpBean.inorStart;
if (tmpSubLeftCounts>0) {
Bean tmpLeftTree = new Bean();
tmpLeftTree.parentVal = tmpRoot.val;
tmpLeftTree.isLeft = true;
tmpLeftTree.preStart = tmpBean.preStart + 1;
tmpLeftTree.preEnd = tmpBean.preStart + tmpSubLeftCounts;
tmpLeftTree.inorStart = i - tmpSubLeftCounts;
tmpLeftTree.inorEnd = i - 1;
tmpBeans.offer(tmpLeftTree);
}
// 如果当前父节点还存在右子树
if (i < tmpBean.inorEnd) {
Bean tmpRightTree = new Bean();
tmpRightTree.parentVal = tmpRoot.val;
tmpRightTree.isLeft = false;
tmpRightTree.preStart = tmpBean.preStart + tmpSubLeftCounts + 1;
tmpRightTree.preEnd = tmpBean.preEnd;
tmpRightTree.inorStart = i + 1;
tmpRightTree.inorEnd = tmpBean.inorEnd;
tmpBeans.offer(tmpRightTree);
}
}
return root;
}
两种方法都是从上往下构建树,先确定父节点,然后构建左右子树。