字节8.16第一题前序中序构造二叉树
题目描述
给定一个二叉树,二叉树每个节点都有一个唯一的正整数值代表节点,在遍历时,我们使用节点的整数值作为标记
输入:二叉树的节点个数,前序和中序遍历结果,分别是第一行,第二行与第三行
输出:二叉树叶子节点的个数
输入描述:
第一行 输入二叉树节点个数N,其中0<N<30000
第二行与第三行分别输入二叉树的前序和中序遍历结果,每个节点对应唯一整数值
3
1 2 3
2 1 3
输出描述:
二叉树叶子节点个数
2
题目理解
在上一篇二叉搜索树的第k个节点中已经谈到了二叉树遍历的顺序是根据访问根节点的顺序定义的,当我们知道前序遍历和中序遍历的结果,如1 2 3 和2 1 3,我们首先根据先序遍历首先访问根节点就可以确定出根节点是1,那么它的左子树和右子树分别是什么呢?我们可以在中序遍历2 1 3中找到根节点1,根节点前面的就是左子树,根节点后面就是右子树.那如果二叉树变复杂了呢?其实是一一样的,就是一个递归(套娃)过程,比如下面的这个二叉树,没错就是上一篇中的二叉搜索树的第k个节点的二叉树,它的前中后序应该都比较熟悉
它的前序:5 3 2 4 7 6 8,中序:2 3 4 5 6 7 8 后序:2 4 3 6 8 7 5
我们根据前序找到根节点是5,在中序中找到根节点5,前面就是中序遍历的左子树2 3 4,后面是中序遍历右子树6 7 8.然后根据左子树长度3,在前序中得到左子树遍历结果3 2 4,前序遍历右子树7 6 8.再递归到左子树中,根据前序确定根节点是3,根据中序确定左子节点是2,右子节点是4,同理右子树按照同样方式确定.那么叶子节点必然是我前序访问到最后一个元素时节点,做相应计数即可.
但是由于数组类型不便于观察,我在后面字符串输入,根据前序和中序,以及后序和中序的遍历结果确定二叉树结构
package Zijie_test;
import java.util.*;
class BinTreeNode {
private int data;
private BinTreeNode leftChild;
private BinTreeNode rightChild;
public BinTreeNode(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public BinTreeNode getLeftChild() {
return leftChild;
}
public void setLeftChild(BinTreeNode leftChild) {
this.leftChild = leftChild;
}
public BinTreeNode getRightChild() {
return rightChild;
}
public void setRightChild(BinTreeNode rightChild) {
this.rightChild = rightChild;
}
}
public class test {
public static int count=0;
//根据前序和中序创建二叉树
public static BinTreeNode createBinTreeByPreIn(int[] pre, int[] infix) {
if (pre.length == 1) {
count++;
return new BinTreeNode(pre[0]);
}
//根据前序第一个字符确定父节点
int father = pre[0];
int i = 0;
int number = pre.length;
//根据中序确定左子树长度,直到在前序找到根节点
while (i < number && infix[i] != father)
{
i++;
}
//左子树长度
int leftNumber = i;
//System.out.println("leftNumber: "+i);
//右子树长度 总长度减去左子树和根节点的个数
int rightNumber = number - i - 1;
//创建二叉树
BinTreeNode node = new BinTreeNode(father);
//左子树遍历创建
if (leftNumber >= 1) {
int[] leftPre=new int[leftNumber];
//arraycopy(原数组名称,原数组起始下标,目标数组名称,目标数组起始下标,截取长度)
System.arraycopy(pre,1,leftPre, 0, leftNumber);
int[] leftInfix=new int[leftNumber];
System.arraycopy(infix,0,leftInfix, 0, leftNumber);
for(int j=0;i<leftNumber;i++) {
System.out.println("leftpre: "+leftPre[j]+"leftInfix: "+leftInfix[j]);
}
node.setLeftChild(createBinTreeByPreIn(leftPre, leftInfix));
}
//右子树遍历创建
if (rightNumber >= 1) {
int[] rightPre=new int[rightNumber];
System.arraycopy(pre,1+leftNumber,rightPre,0, rightNumber);
int[] rightInfix=new int[rightNumber];
System.arraycopy(infix,1+leftNumber,rightInfix, 0, rightNumber);
for(int j=0;i<rightNumber;i++) {
System.out.println("rightPre: "+rightPre[j]+"rightInfix: "+rightInfix[j]);
}
node.setRightChild(createBinTreeByPreIn(rightPre, rightInfix));
}
return node;
}
public static void post_travel(BinTreeNode pRoot) {
if(pRoot!=null) {
post_travel(pRoot.getLeftChild());
post_travel(pRoot.getRightChild());
System.out.println(pRoot.getData());
}
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int size=sc.nextInt();
//过滤掉enter结束符
String xioqu=sc.nextLine();
int[] pre=new int[size];
int[] infix=new int[size];
//String[] str_pre=sc.nextLine().split(" ");
//String[] str_infix=sc.nextLine().split(" ");
String[] str_pre="5 3 2 4 7 6 8".split(" ");
String[] str_infix="2 3 4 5 6 7 8".split(" ");
for(int i=0;i<size;i++) {
pre[i]=Integer.parseInt(str_pre[i]);
infix[i]=Integer.parseInt(str_infix[i]);
//System.out.println("pre: "+pre[i]+"infix: "+infix[i]);
}
BinTreeNode root=createBinTreeByPreIn(pre,infix);
post_travel(root);
System.out.println(count);
}
}
运行结果:
中间是后序遍历结果
字符串的前序和中序结果构建二叉树
已知一颗二叉树的先序序列为EBADCFHG,其中序序列为ABCDEFGH,后序遍历为ACDBGHFE.按照同样的方式构建二叉树在下面直接给出代码,可以对比运行结果观察构建二叉树过程
package Zijie_test;
import java.util.*;
class Bin_TreeNode {
private char data;
private Bin_TreeNode leftChild;
private Bin_TreeNode rightChild;
public Bin_TreeNode(char data) {
this.data = data;
}
public char getData() {
return data;
}
public void setData(char data) {
this.data = data;
}
public Bin_TreeNode getLeftChild() {
return leftChild;
}
public void setLeftChild(Bin_TreeNode leftChild) {
this.leftChild = leftChild;
}
public Bin_TreeNode getRightChild() {
return rightChild;
}
public void setRightChild(Bin_TreeNode rightChild) {
this.rightChild = rightChild;
}
}
public class first_BinaryTree {
public static int count=0;
//根据前序和中序创建二叉树
public static Bin_TreeNode createBinTreeByPreIn(String pre, String infix) {
System.out.println("pre: "+pre+" infix: "+infix);
if (pre.length() == 1) {
count++;
return new Bin_TreeNode(pre.charAt(0));
}
//根据前序第一个字符确定根节点
char c = pre.charAt(0);
int i = 0;
int number = pre.length();
//根据中序确定左子树长度,直到在前序找到根节点
while (i < number && infix.charAt(i) != c)
{
i++;
}
//左子树长度
int leftNumber = i;
System.out.println("leftNumber: "+i);
//右子树长度
int rightNumber = number - i - 1;
//创建二叉树
Bin_TreeNode node = new Bin_TreeNode(c);
//左子树遍历创建
if (leftNumber >= 1) {
String leftPre = pre.substring(1, 1 + leftNumber);
String leftIn = infix.substring(0, leftNumber);
node.setLeftChild(createBinTreeByPreIn(leftPre, leftIn));
}
//右子树遍历创建
if (rightNumber >= 1) {
//substring有两种截取方式,截取的起始位置,或者截取的起始位置和终止位置
String rightPre = pre.substring(leftNumber + 1);
String rightIn = infix.substring(leftNumber + 1);
node.setRightChild(createBinTreeByPreIn(rightPre, rightIn));
}
return node;
}
//根据中序和后序创建二叉树
public static Bin_TreeNode createBinTreeByInPost(String infix , String post ) {
System.out.println("post: "+post+" infix: "+infix);
//访问到叶子节点,就直接创建叶子节点并加入到二叉树中
if(post.length()==1) {
count++;
return new Bin_TreeNode(post.charAt(post.length()-1));
}
//根据后序最后一个字符确定父节点,利用该节点在中序中划分出该节点的左子树和右子树
char c=post.charAt(post.length()-1);
int i=0;
int number=post.length();
while(i<number&&infix.charAt(i)!=c) {
i++;
}
int leftnumber=i;
int rightNumber = number - i - 1;
Bin_TreeNode node = new Bin_TreeNode(c);
//左子树创建
if (leftnumber >= 1) {
//在中序遍历和后序遍历中找出左子树
String leftIn = infix.substring(0,leftnumber);
String leftPost = post.substring(0, leftnumber);
System.out.println("leftpost: "+leftPost+" leftinfix: "+leftIn);
node.setLeftChild(createBinTreeByInPost(leftIn, leftPost));
}
//右子树创建
if (rightNumber >= 1) {
String rightPost = post.substring(leftnumber,post.length()-1);
String rightIn=infix.substring(leftnumber+1);
System.out.println("rightpost: "+rightPost+" rightinfix: "+rightIn);
node.setRightChild(createBinTreeByInPost(rightIn,rightPost));
}
return node;
}
public static void pre_travel(Bin_TreeNode pRoot) {
if(pRoot!=null) {
System.out.println(pRoot.getData());
pre_travel(pRoot.getLeftChild());
pre_travel(pRoot.getRightChild());
}
}
public static void post_travel(Bin_TreeNode pRoot) {
if(pRoot!=null) {
post_travel(pRoot.getLeftChild());
post_travel(pRoot.getRightChild());
System.out.println(pRoot.getData());
}
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
//int N=sc.nextInt();
//String getEnter=sc.nextLine();
//String pre=sc.nextLine();
//String infix=sc.nextLine();
//示例一
//String pre="ABCDEFGHK";
//String infix="BDCAEHGKF";
//String post="DCBHKGFEA";
//示例二
String pre="EBADCFHG";
String infix="ABCDEFGH";
String post="ACDBGHFE";
Bin_TreeNode Root=createBinTreeByPreIn(pre,infix);
//Bin_TreeNode Root=createBinTreeByInPost(infix,post);
pre_travel(Root);
//post_travel(Root);
System.out.println(count);
}
}
运行结果
下图是根据先序和中序构建二叉树过程,中间有输出,最后一列ACDBGHFE是后序遍历结果,根据后序结果我们就能验证是否构建正确,也可以用先序或中序来验证
下面用一张图还原构建二叉树过程(这里图片是参考构建二叉树过程)
首先根据前序序列知道二叉树的根结点为E,则其左子树的中序序列为ABCD,左子树的先序序列是BADC;右子树的中序序列为FGH,右子树先序序列是FHG。同理再重复上述步骤,根据先序确定左子树的父节点是B,再根据左子树中序确定左子节点是A,右子节点先序是DC,中序是CD,就可以确定父节点是D,左子节点是C.按照同样的方式可以分析出右子树
后序和中序构建二叉树
在上面的代码中可以看到有根据后序和中序构建二叉树部分,其实是同样道理,这里一样是根据后序的特性,它最后一个元素是根节点,然后再根据中序确定左子树和右子树,再重复上述步骤构建二叉树.
下图就是根据后序和中序构建二叉树过程,最后一列EBADCFHG是先序遍历结果,大家可以自己观察构建过程.