一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点,
- 其左子树中所有结点的键值小于该结点的键值;
- 其右子树中所有结点的键值大于等于该结点的键值;
- 其左右子树都是二叉搜索树。
所谓二叉搜索树的“镜像”,即将所有结点的左右子树对换位置后所得到的树。
给定一个整数键值序列,现请你编写程序,判断这是否是对一棵二叉搜索树或其镜像进行前序遍历的结果。
输入格式:
输入的第一行给出正整数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
我的思路是 假设它是二叉排序树成立,我就按照二叉排序树的规则以及题目给的先序顺序,利用栈来模拟建树的过程,这里用到了两个栈 s1 和s2;
s1是用来实现 把相应的数插入正确的位置,s2则是和s1同步插入 但是实现模拟树的后序遍历的过程,并且把遍历结果存到队列中;
这里有个mark变量,mark是一个标记
以本体为例,以先序的顺序 插入结点, 最开始 栈里只有一个结点 就是先序的第一个点,把先序下一个点p和s1栈顶比较 如果小于他就放在左边,否则执行while循环 一直pop s1 直到s1栈顶大于 p,在此过程中s2 不是执行pop 而是给相应元素做个标记 temp 意思是 这个元素左子树已经遍历完了 等其右子树遍历完重新pop到这个元素,就是发现已经被标记过一次,就把他放入队列 再pop,实际s2上就是用栈后序遍历的过程;
#include<iostream> #include<vector> #include<stack> #include<queue> #include<stdio.h> using namespace std; class node{ public:int d; int temp; }; //com在这为比较函数,用来区别处理镜像和本体的情况 bool com(int a,int b,int t){ if(t) return a>=b; else return a<b; } int n; int *v; int fun1(int t){ stack<int> s1; stack<node> s2; node nn; queue<int> q; int mark; if(t) mark=-9; else mark=999999; s1.push(v[0]); nn.d=v[0];nn.temp=0; s2.push(nn); for(int i=1;i<n;i++) {//这里有com比较,把结点放到小于等于他的最大节点的右边(本体情况)镜像类推; while(!s1.empty()&&com(v[i],s1.top(),t)) { mark=s1.top(); s1.pop(); //把其被标记的父节点放入输出队列,并删掉 while(s2.top().temp){ q.push(s2.top().d); s2.pop(); } nn=s2.top();s2.pop();nn.temp=1; s2.push(nn); } //这里有com比较 if(com(v[i],mark,!t)) {return 0;} s1.push(v[i]); nn.d=v[i],nn.temp=0,s2.push(nn); } while(!s2.empty()){ nn=s2.top(); q.push(nn.d); s2.pop(); } cout<<"YES"<<endl; cout<<q.front();q.pop(); while(!q.empty()) { cout<<" "<<q.front(); q.pop(); } return 1; } int main(){ //freopen("F:\\1.txt","r",stdin); cin>>n; v=new int[n]; for(int i=0;i<n;i++) cin>>v[i]; if(!fun1(1)) if(!fun1(0))cout<<"NO" ; //fclose(stdin); }