题目要求如下:
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历{4,7,2,1,5,3,8,6},则重建出图2.6所示的二叉树并输出它的头结点。
题目的意思是二叉树已知前序中序遍历,要求重建出这个二叉树,与之相似的题目还有已知前序中序遍历求后根序遍历等等,这些题的核心思想都是一样的,即重点都是:
1.前序遍历的第一个节点为根节点;
2.中序遍历中,根节点的左边所有节点即构成根节点的左子树,根右边的节点构成根节点的右子树;
知道了这个思想,这个题就很好做了。程序的主要步骤如下:
1. 找到这个树的根节点;
2. 在中序遍历中找到这个根节点,将中序遍历分成两个新的中序遍历;
3. 两个新的中序遍历分别去找自己匹配的新的前序遍历;
4. 递归处理直至左(右)子树为空或左子树只有一个节点
实现代码如下:(为了验证树是否重建成功,我选择再次输出他的前序遍历,观察两次是否一样)
package com.offer;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.offer.BinaryTree.BinaryTreeNode;
public class BinaryTree {
static class BinaryTreeNode{
private BinaryTreeNode m_pleft = null;
private BinaryTreeNode m_pright = null;
private int m_value = 0;
public BinaryTreeNode getM_pleft() {
return m_pleft;
}
public void setM_pleft(BinaryTreeNode m_pleft) {
this.m_pleft = m_pleft;
}
public BinaryTreeNode getM_pright() {
return m_pright;
}
public void setM_pright(BinaryTreeNode m_pright) {
this.m_pright = m_pright;
}
public int getM_value() {
return m_value;
}
public void setM_value(int m_value) {
this.m_value = m_value;
}
@Override
public String toString() {
return "BinaryTreeNode [ m_value=" + m_value + "]";
}
}
public static void main(String[] args) {
int[] front = {1,2,4,7,3,5,6,8};
int[] middle = {4,7,2,1,5,3,8,6};
BinaryTreeNode node = findTree(front , middle);
disPlay(node);
}
private static void disPlay(BinaryTreeNode root){
//前序遍历递归版
//1.访问根
//2.访问左子树
//3.访问右子书
//这里要用的是if
//不是while!!!!
//while中需要改变括号里的值,否则永远为真不会结束!!!
if(root != null){
System.out.println(root.getM_value());
disPlay(root.getM_pleft());
disPlay(root.getM_pright());
}
}
public static BinaryTreeNode findTree(int[] front , int[] middle){
if (front == null || middle == null) {
return null;
}
if (front.length == 0) {
//当长度为1时直接返回不用再分了
BinaryTreeNode newNode = null;//注意:这里不能写成 = new BinaTreeNode! 否则遍历此树时无法结束
return newNode;
}
if (front.length == 1) {
BinaryTreeNode newNode = new BinaryTreeNode();
newNode.setM_value(front[0]);
// newNode.setM_pleft(null);
// newNode.setM_pright(null);
return newNode;
}
BinaryTreeNode root = new BinaryTreeNode(); //注意,必须直接把内部类写成静态的,否则必须要先实例化外部类才可以。
root.setM_value(front[0]);//前序第一个数为这个树的根
//寻找树的根在中序遍历中的位置
//方案一:
int rootNumberIndex = 0;
for (int i = 0; i < middle.length; i++) {
if (middle[i] == front[0]) {
rootNumberIndex = i;
}
}
//新的左边的中序
int[] newLeftMiddle = null;
newLeftMiddle = Arrays.copyOf(middle, rootNumberIndex );
System.out.println("新左中序" +Arrays.toString(newLeftMiddle));
//新的右边的中序
int[] newRightMiddle = null;
newRightMiddle = Arrays.copyOfRange(middle, rootNumberIndex + 1, middle.length);
System.out.println("新右中序" + Arrays.toString(newRightMiddle));
//新的左边的前序遍历
int[] newLeftFront = new int[newLeftMiddle.length];
//这块有错!!!
//这里是要找到front里面找到newLeftMiddle的顺序组成newLeftFront
int k = 0;
for (int j = 0; j < front.length; j++) {
if(judgeContain(newLeftMiddle,front[j])){
newLeftFront[k] = front[j];
k++;
}
}
System.out.println("新左前序" + Arrays.toString(newLeftFront));
//新的右边的前序遍历
int[] newRightFront = new int[newRightMiddle.length];
int k2 = 0;
for (int j = 0; j < front.length; j++) {
if (judgeContain(newRightMiddle , front[j])) {
newRightFront[k2] = front[j];
k2++;
}
}
System.out.println("新右前序" + Arrays.toString(newRightFront));
root.setM_pleft(findTree(newLeftFront , newLeftMiddle));
root.setM_pright(findTree(newRightFront , newRightMiddle));
return root;
}
private static boolean judgeContain(int[] intIndex, int number) {
// TODO to judge the intIndex if have the number
for (int i : intIndex) {
if( i == number){
return true;
}
}
return false;
}
}
代码运行结果如下:
题目扩展:有一个与之相似的题目,已知一棵二叉树的中序和后序遍历,求其的前序遍历,其实大概思路是一样的,为一不同的是这个后序遍历最后一个节点是根节点罢了。读者可以自行尝试。