一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点,
- 其左子树中所有结点的键值小于该结点的键值;
- 其右子树中所有结点的键值大于等于该结点的键值;
- 其左右子树都是二叉搜索树。
所谓二叉搜索树的“镜像”,即将所有结点的左右子树对换位置后所得到的树。
给定一个整数键值序列,现请你编写程序,判断这是否是对一棵二叉搜索树或其镜像进行前序遍历的结果。
输入格式:
输入的第一行给出正整数 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
什么样才是二叉搜素树呢?
假如 5 3 2 4 7 6 8,指针l = 1,r = 7。
l向右移动,找第一个大于root(5)的数,最后停在了7。
r向左移动,找第一个小于root(5)的数,最后停在了4。
此时l == r+1,如果画图我们知道l刚好是root的孩子之一,root另一个孩子就是root+1所对应的节点。
因此我们只要以两个新的父节点在子树范围内找即可。
字数范围是多少呢?假如我们把树画出来,可以知道3 对应的子树是:(3,2,4)。7对应的子树是(7,6,8)。
即( l+1,r)和 (l,R)
此后递归即可。
而为了输出后续的遍历结果。我们要在找到左右子节点后存储节点,有点类似于遍历节点,同样的要输出前序遍历的结果,只要把先存储结点的位置,之后再找子树。
完整代码:
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = a; i <= b; ++ i)
#define per(i, a, b) for(int i = a; i >= b; -- i)
using namespace std;
int a[1010];
vector<int> ans;
int style;
void findRoot(int L,int R){
if(L > R) return;
int l = L+1,r = R;
if(style){
while(l <= R && a[l] < a[L]) l++;
while(r > L && a[r] >= a[L]) r--;
}else{
while(l <= R && a[l] >= a[L]) l++;
while(r > L && a[r] < a[L]) r--;
}
if(l - 1 != r) return;
ans.push_back(a[L]);
findRoot(L+1,r);
findRoot(l,R);
}
int main(){
int n; cin >> n;
rep(i,1,n) cin >> a[i];
findRoot(1,n);
if(ans.size() != n){
style = 1;
ans.clear();
findRoot(1,n);
}
if(ans.size() != n) cout << "NO\n";
else{
cout << "YES\n" << ans[0];
rep(i,1,ans.size()-1) cout << " " << ans[i];
cout << endl;
}
return 0;
}