package com.areio.offer;
public class TreeNode {
//每一个结点要包含左指针,右指针,当前结点的值
int val;
TreeNode left = null;
TreeNode right = null;
//把左右节点设为null,构造新结点时不用自己设置
public TreeNode(int val){
this.val=val;
}
}
package com.areio.offer;
import static com.areio.offer._7A_TreeVisit.preOrder1;
/*
* 题目:输入某二叉树的前序和中序遍历,重构二叉树(假设输入的前序遍历和中序遍历未含重复数字)。
* 例如前序遍历{1,2,4,7,3,5,6,8},{4,7,2,1,5,3,8,6},其二叉树为
* 1
* 2 3
* 4 5 6
* 7 8
*
*
* 测试用例:
* 1、特殊输入:前序中序矛盾——构不成二叉树;二叉树根节点为null
* 2、特殊二叉树:只有左子树;只有右子树;只有一个结点
* 3、普通二叉树:完全二叉树;不完全二叉树
*
* 思路:
* 1、根据前序遍历得到根节点root,构建根
* 2、根据root去中序遍历得到其左子树和右子树,构建左右子树
* 3、左右子树的构建与1、2一样,可用递归调用当前函数
*
* */
public class _7_RebuildTree {
/*
* preOder:前序遍历
* inOrder:中序遍历
* length:元素个数
* */
public static TreeNode constructor(int[] preOrder,int[] inOrder,int length){
if (preOrder==null||inOrder==null||length<=0) return null;
return rebuildTree(preOrder,0,preOrder.length-1,inOrder,0,inOrder.length-1);
}
/*
构建一棵树,该函数需要被子树复用,下次子树要构建自己,需要将自己孩子的范围传进来
* startPreOrder:当前子树的前序遍历的起始索引
* endPreOrder:当前子树的前序遍历的终止索引
* */
public static TreeNode rebuildTree(int[] preOrder,int startPreOrder,int endPreOrder,int[] inOrder,int startInOrder,int endInOrder){
//1、从前序遍历中找到根节点,并构建该结点
int rootVal=preOrder[startPreOrder];
TreeNode root=new TreeNode(rootVal);
//2、从中序遍历找到该结点根结点
int rootIndex=startInOrder;
while (rootIndex<=endInOrder && inOrder[rootIndex]!=rootVal){//要么是因为到最后一个元素了,要么是找到了根了才会退出循环
rootIndex++;
}
//是否在中序遍历有找到根节点
//因为到了最后一个元素退出的循环——中序遍历中没有根节点
if (rootIndex==endInOrder+1){
try {
throw new Exception("前序中序遍历不匹配");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//到这里说明是在中序遍历里找到了根了
//3、确定根结点的左右子树
//计算根左右子树的前序遍历的起止位置,在中序遍历的起止位置
int leftTreeLength=rootIndex-startInOrder;//左子树长度
int rightTreeLength=endInOrder-rootIndex;//右子树长度
//子树在前序遍历的起始位置
int leftPreOrderStart=startPreOrder+1;
//子树在前序遍历的终止位置
int leftPreOrderEnd=startPreOrder+leftTreeLength;
//4、构建左子树,传入左子树在前序遍历的起止位置,在中序遍历的起止位置
if (leftTreeLength>0) {//当左子树右子树为空则自己结束递归
root.left=rebuildTree(preOrder, leftPreOrderStart, leftPreOrderEnd,inOrder,startInOrder,rootIndex-1);
}
//5、构建右子树
if (rightTreeLength>0){
root.right=rebuildTree(preOrder,leftPreOrderEnd+1,endPreOrder,inOrder,rootIndex+1,endInOrder);
}
//如果只有一个结点,则直接返回根结点
return root;
}
public static void main(String[] args) {
//不完全二叉树
int[] preOrder = new int[] {1,2,4,7,3,5,6,8};
int[] inOrder = new int[] {4,7,2,1,5,3,8,6};
//构不成二叉树
// int[] preOrder = new int[] {1,2,4,7,3,5,6,8};
// int[] inOrder = new int[] {8,8,2,1,5,3,8,6};
//null
// int[] preOrder = new int[] {};
// int[] inOrder = new int[] {};
//只有左子树
// int[] preOrder = new int[] {1,2,4,7};
// int[] inOrder = new int[] {7,4,2,1};
TreeNode root = constructor(preOrder, inOrder, preOrder.length);
System.out.println("输入的前序遍历:");
for (int i:preOrder){
System.out.print(i+" ");
}
System.out.println();
//打印出该树的前序遍历验证是否与输入的一样
System.out.println("重构树的前序遍历:");
preOrder1(root);
}
}
前序遍历的方法查看:二叉树的遍历(前中后层次)https://mp.csdn.net/mp_blog/creation/editor/122100011