L2-004. 这是二叉搜索树吗?
一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点,
- 其左子树中所有结点的键值小于该结点的键值;
- 其右子树中所有结点的键值大于等于该结点的键值;
- 其左右子树都是二叉搜索树。
所谓二叉搜索树的“镜像”,即将所有结点的左右子树对换位置后所得到的树。
给定一个整数键值序列,现请你编写程序,判断这是否是对一棵二叉搜索树或其镜像进行前序遍历的结果。
输入格式:
输入的第一行给出正整数N(<=1000)。随后一行给出N个整数键值,其间以空格分隔。
输出格式:
如果输入序列是对一棵二叉搜索树或其镜像进行前序遍历的结果,则首先在一行中输出“YES”,然后在下一行输出该树后序遍历的结果。数字间有1个空格,一行的首尾不得有多余空格。若答案是否,则输出“NO”。
题意:略
思路:首先通过输入的前两个数字判断这棵二叉树是否是“镜像”。如果num[0] > num[1]则不是“镜像”,否则是“镜像”。然后,下
一步判断左子树的元素和右子树的元素。以非“镜像”搜索二叉树和非二叉搜索树为例。
例如:8 6 5 7 10 8 11中8是输入的第一个元素它就整棵树的根,然后发现10是第一个大于等于8的元素所以。作为根的8和10之间的元素便是左子树,从10到末尾的元素就是右子树。在子树中的操作也相同。
在 8 6 8 5 10 9 11中第二个元素6比第一个元素8小则该树不是“镜像”搜索二叉树。第三个元素8是第一个大于等于根的元素。则第一个元素与第三个元素之间的元素就是根8的左子树。在第三个元素之后包括它本身的就是根的右子树。但是,在右子树上有一个元素5小于该子树的根。所以,它不是二叉搜索树。
然后剩下的就是在写的时候注意细节就行了。
下面是我的AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define MAX 1005
using namespace std;
int n,num[MAX], ans[MAX],aa;
bool ok,OK;
bool Judge(bool a){return a == ok;}
bool Judge2(int now,int head,int rear)
{
return !(rear - head < -1);
}
void Put(int now, int head,int rear)
{
// getchar();
// cout << now << "/" << head << "/" << rear << endl;
int j,i;
if(head >= n || rear >= n || now >= n) return; //如果已经越界则直接返回。
for(j = head; j <= rear; j++){
if(!Judge(num[j] < num[now])){ //因为“镜像”和“非镜像”是在这里的判断相反,所以需要Judge函数来判断。
break;
}
}
for(i = j; i <= rear; i++)
if(Judge(num[i] < num[now])) return;
//因为这棵(子)树是以now为根的子树,now到j之间的元素就是对应的左子树,j到rear之间的元素就是对应的右子树
if(Judge2(head,head+1,j-1)){ //在这里使用Judge2就是一个细节上的处理没有这个判断会被 5 1 6 9 8 7 这组数据卡住。
Put(head,head+1,j-1);
ans[aa++] = num[head];
}
if(Judge2(j,j+1,rear)){
Put(j,j+1,rear);
ans[aa++] = num[j];
}
return;
}
int main( )
{
while(cin >> n){
memset(num,0,sizeof(num));
for(int j = 0; j < n; j ++)
cin >> num[j];
if(num[0] > num[1]) ok = true; //通过前两个元素判断是否是“镜像”
else ok = false;
aa = 0;
memset(ans,0,sizeof(ans));
Put(0,1,n-1);
ans[aa++] = num[0];
if(aa < n-1){
cout << "NO" << endl;
}else{
cout << "YES" << endl;
cout << ans[0];
for(int j = 1; j < aa; j++)
cout << " " << ans[j];
cout << endl;
}
}
return 0;
}
我刚开始看完的第一反应就是模拟而以下二叉树就行了,但是,一想那样做有些麻烦。于是,就自己想了一个在debug中更麻烦的做法......在AC后上网查了一下别人代码思路上也是模拟。但是,他们很多是还原二叉树然后在进行一系列的操作。我感觉这样有些小小的麻烦。
L2-011. 玩转二叉树
给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。
输入格式:
输入第一行给出一个正整数N(<=30),是二叉树中结点的个数。第二行给出其中序遍历序列。第三行给出其前序遍历序列。数字间以空格分隔。
输出格式:
在一行中输出该树反转后的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。
题意:略
思路:基本思路和上面题一样也是模拟。如果只是单纯的输出层序遍历很容易。但是,要输出反转后的层序遍历这个就有点麻烦了。只是代码实现也和上面的做法差不多。也是确定左子树和右子树是哪段数组。
下面是我的AC代码:
#include<iostream>
#define MAX 35
using namespace std;
int num1[MAX],num2[MAX];
int n;
struct tree{
int head,rear; //结构体中定义的是这个棵子树在数组上处于haed~rear之间。
int num; //其中num不是最后输出的数字,而是存在于num2数组的下标。
};
tree que[MAX];
void BFS( )
{
int head = 0,rear = 0;
tree now = {0,n-1,0};
que[rear++] = now;
tree next;
while(head < rear){
now = que[head++];
if(now.head >= now.rear) continue;
for(int j = now.head; j <= now.rear; j++){
if(num1[j] == num2[now.num]){
if(j < now.rear){ //因为要输出的是反转后的层序遍历,所以,加入队列的顺序也有所不同。
next.head = j+1;
next.rear = now.rear;
next.num = now.num + j - now.head + 1;
que[rear++] = next;
}
if(j > now.head){
next.head = now.head;
next.rear = j - 1;
next.num = now.num + 1;
que[rear++] = next;
}
break;
}
}
}
return;
}
int main( )
{
while(cin >> n){
for(int j = 0; j < n; j++)
cin >> num1[j];
for(int j = 0; j < n; j++)
cin >> num2[j];
BFS();
//cout << que[0].num;
/*for(int j = 1; j < n; j++)
cout << " " << que[j].num;
cout << endl;*/
cout << num2[que[0].num];
for(int j = 1; j < n; j++)
cout << " " << num2[que[j].num];
cout << endl;
}
}
在AC后我也在网上看了其他人的代码其中有一份代码相当简短。
来源:http://m.blog.csdn.net/article/details?id=52137710
#include <cstdio>
#include <vector>
using namespace std;
vector<int> in, pre, level(100000, -1);
void levelorder(int root, int start, int end, int index) {
if(start > end) return ;
int i = start;
while(i < end && in[i] != pre[root]) i++;
level[index] = pre[root];
levelorder(root + 1, start, i - 1, 2 * index + 2);
levelorder(root + 1 + i - start, i + 1, end, 2 * index + 1);
}
int main() {
int n, cnt = 0;
scanf("%d", &n);
in.resize(n);
pre.resize(n);
for(int i = 0; i < n; i++) scanf("%d", &in[i]);
for(int i = 0; i < n; i++) scanf("%d", &pre[i]);
levelorder(0, 0, n-1, 0);
for(int i = 0; i < level.size(); i++) {
if(level[i] != -1 && cnt != n - 1) {
printf("%d ", level[i]);
cnt++;
} else if(level[i] != -1){
printf("%d", level[i]);
break;
}
}
return 0;
}
因为,上学期上数据结构课的时候光想着睡觉了,结果看到这个题。就连什么是前序、中序、后序、层序遍历都废了很大的劲。以至于现在看上面这个神奇的代码琢磨了一天才琢磨明白。QAQ......