天天看點

PAT 兩個二叉樹的模拟題 L2-004,L2-011 L2-011. 玩轉二叉樹

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......

繼續閱讀