每日一题----[USACO3.4] 美国血统 American Heritage
这个题实际上就是根据树的前序遍历和中序遍历来构造这颗树,同时根据构造结果得到后续遍历结果
如果对于与树遍历相关的基础知识不了解的话可以去看看这篇博客
和树相关的题目基本上都可以通过递归去解决,这个题也是一样的,首先需要确保自己能根据前序遍历和中序遍历的结果可以手动构造出来这棵树,然后再是找到规律通过代码去实现
- 对于中序遍历来说,首先找到根节点,根节点的左边是树的左子树,根节点的右边是它的右子树
- 对于前序遍历来说,第一个结点就是这棵子树的根节点,右侧紧挨着的x位为其左子树,再右边的y位为其右子树
那么我们可以根据这个去实现由前序遍历和中续遍历恢复整棵树,前序遍历的x和y可以根据中序遍历左边和右边结点的个数来确定
对于给定的前序遍历序列和中序遍历序列,我们可以进行如下处理
- 首先找到当前子树的根节点在中序遍历序列中的位置
- 由中序遍历的位置得到当前根节点左子树和右子树的长度
- 确定左子树和右子树的序列在前序遍历中的位置
- 递归构造左子树和右子树
- 打印当前结点
这里可能最后会有疑问明明是构造树为什么没有保存,并且还在最后打印了当前结点?
- 实际上这里这是概念上的构造树,也就是我们根据前序遍历和中序遍历去dfs遍历了一遍树,然后树的前序遍历,中序遍历,后序遍历实质的区别只是打印当前结点的位置不同而已。
- 前序遍历是先打印当前结点再遍历左右子树;中序遍历是先遍历左子树再打印当前结点再遍历右子树;后续遍历是先遍历左右子树再打印当前结点。主要的思想都是dfs遍历
- 这里也是用的这个想法,根据前序遍历和中序遍历去dfs遍历这颗树,然后在遍历完左右子树之后再打印当前结点也就如同直接进行后续遍历一样了
实际上,我们不仅可以根据前序遍历和中序遍历去构造一棵树,也可以根据后序遍历和中序遍历去构造这棵树,但是不能根据前序遍历和后序遍历去构造一棵树,有兴趣的可以再自己实现一下根据后序遍历和中序遍历去构造一颗树
package cn.edu.xjtu.daily.April.day_4_12;
import java.util.Scanner;
/**
* @author Hydrion-QLz
* @date 2022-04-12 0:06
* @description https://www.luogu.com.cn/problem/P1827
*/
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String inOrder = sc.next();
String preOrder = sc.next();
sc.close();
// 构造整个树
construct(preOrder, inOrder, 0, preOrder.length(), 0, inOrder.length());
}
/**
* 构造当前范围内的子树,中序序列中的范围:[inOrderLeft, inOrderRight),前序序列中的范围:[preOrderLeft, preOrderRight)
*
* @param preOrder 前序遍历序列
* @param inOrder 中序遍历序列
* @param inOrderLeft 中序遍历序列的左边界,包含
* @param inOrderRight 中序遍历序列的右边界,不包含
* @param preOrderLeft 前序遍历的左边界,包含
* @param preOrderRight 前序遍历的右边界,不包含
*/
private static void construct(String preOrder, String inOrder, int inOrderLeft, int inOrderRight, int preOrderLeft, int preOrderRight) {
if (inOrderLeft == inOrderRight) {
// 当区间长度为0表示没有元素了
return;
}
String inOrderSubStr = inOrder.substring(inOrderLeft, inOrderRight);//当前子树的所有结点的中序序列
char rootCh = preOrder.charAt(preOrderLeft);// 当前子树根节点的字符
int rootIdx = inOrderSubStr.indexOf(rootCh) + inOrderLeft;// 当前子树的根节点在中序序列中的下标
int leftSubTreeLength = rootIdx - inOrderLeft;// 左子树长度
// 构造左子树
construct(preOrder, inOrder, inOrderLeft, rootIdx, preOrderLeft + 1, preOrderLeft + 1 + leftSubTreeLength);
// 构造右子树
construct(preOrder, inOrder, rootIdx + 1, inOrderRight, preOrderLeft + 1 + leftSubTreeLength, preOrderRight);
System.out.print(rootCh);
}
}