一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点,
- 其左子树中所有结点的键值小于该结点的键值;
- 其右子树中所有结点的键值大于等于该结点的键值;
- 其左右子树都是二叉搜索树。
所谓二叉搜索树的“镜像”,即将所有结点的左右子树对换位置后所得到的树。
给定一个整数键值序列,现请你编写程序,判断这是否是对一棵二叉搜索树或其镜像进行前序遍历的结果。
输入格式:
输入的第一行给出正整数 N(≤1000)。随后一行给出 N 个整数键值,其间以空格分隔。
输出格式:
如果输入序列是对一棵二叉搜索树或其镜像进行前序遍历的结果,则首先在一行中输出 YES ,然后在下一行输出该树后序遍历的结果。数字间有 1 个空格,一行的首尾不得有多余空格。若答案是否,则输出 NO。
输入样例 1:
7
8 6 5 7 10 8 11
输出样例 1:
YES
5 7 6 8 11 10 8
输入样例 2:
7
8 10 11 8 6 7 5
输出样例 2:
YES
11 8 10 7 5 6 8
输入样例 3:
7
8 6 8 5 10 9 11
输出样例 3:
NO
解题思路
1. 理解二叉搜索树(BST)的性质
- 对于BST,任何节点的左子树中的所有元素值都小于该节点的值;右子树中的所有元素值都大于等于该节点的值。
- 对于BST的镜像,条件相反:左子树的值都大于等于节点值,右子树的值都小于节点值。
2. 理解前序遍历和后序遍历
- 前序遍历:先访问根节点,然后递归地前序遍历左子树,接着递归地前序遍历右子树。
- 后序遍历:先递归地后序遍历左子树,然后递归地后序遍历右子树,最后访问根节点。
3. 分析问题
问题要求判断一个序列是否为BST或其镜像的前序遍历结果。这意味着我们需要尝试根据给定序列构建出原BST或其镜像,并在构建的过程中验证序列是否符合BST的性质。如果能够成功构建,则序列有效;否则,序列无效。
4. 解决方案
- 构建和验证:从序列的第一个元素开始,它必定是树的根节点。接下来,序列被分为两部分,左子树的元素和右子树的元素。对于正常的BST,第一个大于根节点值的元素之前的所有元素构成左子树,之后的所有元素构成右子树;对于镜像树,第一个小于根节点值的元素之前的所有元素构成右子树,之后的所有元素构成左子树。根据这一规则,递归地构建树的每个节点,并在构建过程中验证给定序列是否满足BST的性质。
- 生成后序遍历序列:在构建树的过程中,同时记录后序遍历序列。完成树的构建后,如果序列是有效的,我们就得到了树的后序遍历结果。
5. 实现细节
- 使用递归方法尝试构建BST及其镜像,同时进行序列的有效性验证。
- 使用全局变量或类成员变量记录后序遍历的结果。
- 根据构建和验证的结果输出相应的信息("YES"或"NO"),以及后序遍历序列(如果存在)。
6. 注意事项
- 输入序列可能不仅仅是一棵简单的BST的前序遍历结果,也可能是其镜像的前序遍历结果,因此需要对这两种情况都进行尝试。
- 需要正确处理边界条件,例如空树或只有单个节点的树。
- 验证序列时,要注意根据当前节点的值更新子树的值范围,确保构建的过程中子树满足BST或其镜像的性质。
解题代码
import java.util.Scanner;
import java.util.ArrayList;
class BinarySearchTree {
static class TreeNode {
int value;
TreeNode left, right;
TreeNode(int value) {
this.value = value;
}
}
/**
* 保存输入的前序遍历序列
*/
private ArrayList<Integer> preOrder;
/**
* 用于存储生成的后序遍历序列
*/
private ArrayList<Integer> postOrder = new ArrayList<>();
/**
* 指示当前构建的是否为镜像二叉搜索树
*/
private boolean isMirror;
/**
* 初始化二叉搜索树的前序遍历序列和是否为镜像树的标志
* @param preOrder
* @param isMirror
*/
public BinarySearchTree(ArrayList<Integer> preOrder, boolean isMirror) {
this.preOrder = preOrder;
this.isMirror = isMirror;
}
/**
* 使用递归辅助方法 generatePostOrder 来尝试根据前序遍历序列构建二叉搜索树或其镜像,并生成后序遍历序列。如果成功构建,则返回 true;否则返回 false。
* @return
*/
public boolean generatePostOrder() {
int[] index = new int[]{0};
TreeNode root = generatePostOrder(preOrder, index, Integer.MIN_VALUE, Integer.MAX_VALUE);
return root != null && index[0] == preOrder.size();
}
/**
* 用于根据前序遍历序列构建二叉树并生成后序遍历序列。方法通过比较节点值和给定的最小、最大值范围来判断当前值是否可以作为二叉搜索树的一部分。
* 通过递归构建左子树和右子树,最后将节点值添加到后序遍历序列中。
* @param preOrder
* @param index
* @param minValue
* @param maxValue
* @return
*/
private TreeNode generatePostOrder(ArrayList<Integer> preOrder, int[] index, int minValue, int maxValue) {
if (index[0] == preOrder.size()) {
return null;
}
int value = preOrder.get(index[0]);
if (value < minValue || value > maxValue) {
return null;
}
TreeNode node = new TreeNode(value);
index[0]++;
if (isMirror) {
node.right = generatePostOrder(preOrder, index, value, maxValue);
node.left = generatePostOrder(preOrder, index, minValue, value - 1);
} else {
node.left = generatePostOrder(preOrder, index, minValue, value - 1);
node.right = generatePostOrder(preOrder, index, value, maxValue);
}
postOrder.add(node.value);
return node;
}
public ArrayList<Integer> getPostOrder() {
return postOrder;
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
ArrayList<Integer> preOrder = new ArrayList<>();
for (int i = 0; i < n; i++) {
preOrder.add(scanner.nextInt());
}
// 尝试作为正常的BST
// 输入序列是对一棵「二叉搜索树」或「镜像」进行前序遍历
BinarySearchTree bst = new BinarySearchTree(preOrder, false);
if (!bst.generatePostOrder()) {
bst = new BinarySearchTree(preOrder, true); // 尝试作为镜像的BST
if (!bst.generatePostOrder()) {
System.out.println("NO");
return;
}
}
System.out.println("YES");
ArrayList<Integer> postOrder = bst.getPostOrder();
for (int i = 0; i < postOrder.size(); i++) {
System.out.print(postOrder.get(i) + (i < postOrder.size() - 1 ? " " : "\n"));
}
}
}