一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点,
- 其左子树中所有结点的键值小于该结点的键值;
- 其右子树中所有结点的键值大于等于该结点的键值;
- 其左右子树都是二叉搜索树。
所谓二叉搜索树的“镜像”,即将所有结点的左右子树对换位置后所得到的树。
给定一个整数键值序列,现请你编写程序,判断这是否是对一棵二叉搜索树或其镜像进行前序遍历的结果。
输入格式:
输入的第一行给出正整数
N
(
≤
1000
)
N(≤1000)
N(≤1000)。随后一行给出
N
N
N 个整数键值,其间以空格分隔。
输出格式:
如果输入序列是对一棵二叉搜索树或其镜像进行前序遍历的结果,则首先在一行中输出
Y
E
S
YES
YES ,然后在下一行输出该树后序遍历的结果。数字间有
1
1
1 个空格,一行的首尾不得有多余空格。若答案是否,则输出
N
O
NO
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
首先明确前序遍历和后序遍历:
- 前序遍历: 根节点 ==>> 左子树 ==>> 右子树 (从左到右)
- 后序遍历: 左子树 ==>> 右子树 ==>> 根节点 (从左向右)
如下图所示:
- 前序遍历
- 后序遍历
按照题目描述,给出的是正常的二叉树的前序遍历或者镜像后的二叉树的前序遍历,所以我们只需要根据给出的前序遍历序列来确定后序遍历序列就可以了,并不需要建一棵树。
我们可以使用双指针,比如命名为Left
和Right
,然后按照输入的序列进行搜索。
并且要区分开镜像和正常二叉树的搜索流程。
对于正常的二叉树的搜索模拟:
- 对于
Left
的搜索:首先从根节点+1的地方开始搜,只要是当前所指的元素的值小于根节点的值,那么就继续往右走,则最后会指向一个小于根节点的最靠右的值(按照前序遍历顺序的)的下一个值,即根节点的右子树的根节点。 - 对于
Right
的搜索:首先从最大的那一端开始搜(即前序遍历的最后一个元素),如果当前所指元素大于等于根节点,那么就继续往左扫,就相当于按照前序遍历顺序逆流,最终会指向大于等于根节点的最靠左的值(按照前序遍历顺序的)的上一个值。 - 搜到最后,如果
Left
和Right
的数值恰好相差了1,即Left
指到了右子树的根节点,Right
指到了左子树的右叶节点,也就是说,我们现在确定了左子树的区间范围和右子树的区间范围 - 那么下一步就按照以上步骤一步步递归求每一个左子树和右子树(根据后序遍历顺序,先搜左子树再搜右子树),等到在递归中搜到了树的根节点甚至都大于叶节点的时候,或者
Left
和Right
的指向之间已经并不恰好相差1的时候,就直接结束这一层 - 在每一层的最后都要将对应的根节点压入答案数组,因为按照这种递归顺序压入,是按照后序遍历顺序,首先压入了左子树的所有节点后,又压入了所有的右子树的所有节点,最终回归到最初的一层,也就在最后压入了根节点
而对于镜像的二叉树,就相当于正常二叉树的右子树和左子树的性质发生了调换,仅仅需要改变一下Left
和Right
搜索的条件就可以,注意后序遍历顺序当然还是和正常的二叉树一样先左后右。
代码:
#include<iostream>
#include<vector>
using namespace std;
const int N = 1010;
int a[N]; //读入
vector<int>ans; //存答案
bool flag = 0; //标记是否镜像搜索
void find(int head, int last) { //head根节点,last前序遍历序列的最后
if (head > last)return; //如果根节点编号比叶节点大直接return
int Left = head + 1; //定义指针Left为根节点下一个点
int Right = last; //定义Right既为Last
if (!flag) { //正常二叉树搜索
while (Left <= last && a[Left] < a[head])Left++; //这里Left取可以等于最终元素下标,便于判断
while (Right > head && a[Right] >= a[head])Right--;
}
else { //镜像二叉树搜索
while (Left <= last && a[Left] >= a[head])Left++;
while (Right > head && a[Right] < a[head])Right--;
}
if (Left - Right != 1)return;
find(head + 1, Right); //搜左子树
find(Left, last); //搜右子树
ans.push_back(a[head]); //压入当前层根节点
}
int main() {
int n; cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
find(1, n);
if (ans.size() != n) { //如果第一次搜索出来不对,就进行镜像搜索
flag = 1;
ans.clear(); //清除答案数组
find(1, n);
}
if (ans.size() != n)cout << "NO" << endl;
else {
cout << "YES\n";
for (int i = 0; i < ans.size(); i++) {
if (i == ans.size() - 1)cout << ans[i];
else cout << ans[i] << " ";
}
}
return 0;
}