PAT 两个二叉树的模拟题 L2-004,L2-011

版权声明:本文原创如果喜欢,欢迎转载。^_^ https://blog.csdn.net/ccutyear/article/details/62886900

L2-004. 这是二叉搜索树吗?

一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点,

  • 其左子树中所有结点的键值小于该结点的键值;
  • 其右子树中所有结点的键值大于等于该结点的键值;
  • 其左右子树都是二叉搜索树。

所谓二叉搜索树的“镜像”,即将所有结点的左右子树对换位置后所得到的树。

给定一个整数键值序列,现请你编写程序,判断这是否是对一棵二叉搜索树或其镜像进行前序遍历的结果。

输入格式:

输入的第一行给出正整数N(<=1000)。随后一行给出N个整数键值,其间以空格分隔。

输出格式:

如果输入序列是对一棵二叉搜索树或其镜像进行前序遍历的结果,则首先在一行中输出“YES”,然后在下一行输出该树后序遍历的结果。数字间有1个空格,一行的首尾不得有多余空格。若答案是否,则输出“NO”。


题意:

思路:首先通过输入的前两个数字判断这棵二叉树是否是“镜像”。如果num[0] > num[1]则不是“镜像”,否则是“镜像”。然后,下

一步判断左子树的元素和右子树的元素。以非“镜像”搜索二叉树和非二叉搜索树为例。

  例如:8 6 5 7 10 8 118是输入的第一个元素它就整棵树的根,然后发现10是第一个大于等于8的元素所以。作为根的810间的元素便是左子树,从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......

猜你喜欢

转载自blog.csdn.net/ccutyear/article/details/62886900