L2-004 這是二叉搜尋樹嗎?

Enjoy_process發表於2019-03-26

                                            L2-004 這是二叉搜尋樹嗎?

                        https://pintia.cn/problem-sets/994805046380707840/problems/994805070971912192

 

 

題意

一棵二叉搜尋樹可被遞迴地定義為具有下列性質的二叉樹:對於任一結點,其左子樹中所有結點的鍵值小於該結點的鍵值;其右子樹中所有結點的鍵值大於等於該結點的鍵值;其左右子樹都是二叉搜尋樹。所謂二叉搜尋樹的“映象”,即將所有結點的左右子樹對換位置後所得到的樹。給定一個整數鍵值序列,現請你編寫程式,判斷這是否是對一棵二叉搜尋樹或其映象進行前序遍歷的結果。

輸入

輸入的第一行給出正整數 N(≤1000)。隨後一行給出 N 個整數鍵值,其間以空格分隔。

輸出

如果輸入序列是對一棵二叉搜尋樹或其映象進行前序遍歷的結果,則首先在一行中輸出 YES ,然後在下一行輸出該樹後序遍歷的結果。數字間有 1 個空格,一行的首尾不得有多餘空格。若答案是否,則輸出 NO

樣例輸入

1)

7
8 6 5 7 10 8 11

2)

7
8 10 11 8 6 7 5

3)

7
8 6 8 5 10 9 11

樣例輸出

1)

YES
5 7 6 8 11 10 8

2)

YES
11 8 10 7 5 6 8

3)

NO

分析

使用前序遍歷和中序遍歷構造樹,如果一顆樹是而二叉搜尋樹,則它的中序遍歷是非遞減的,如果一棵樹是二叉搜尋樹的映象樹,那麼它的中序遍歷是非遞增的,我們可以通過這個性質以及題目給出的前序遍歷求出樹的中序遍歷,先構造二叉搜尋樹,如果失敗的話再進行構造二叉搜尋樹的映象樹,如果還是失敗就輸出“NO”,否則輸出“YES”和構造成的樹的中序遍歷。

 

一棵二叉搜尋樹可被遞迴地定義為具有下列性質的二叉樹:對於任一結點,其左子樹中所有結點的鍵值小於該結點的鍵值;其右子樹中所有結點的鍵值大於等於該結點的鍵值;其左右子樹都是二叉搜尋樹。那麼二叉搜尋樹的映象樹的可這樣定義:對於任一結點,其左子樹中所有結點的鍵值大於等於該結點的鍵值;其右子樹中所有結點的鍵值小於該結點的鍵值;其左右子樹都是二叉搜尋樹的映象樹。

 

由於題目給出的結點鍵值可能是一樣的,因此在構造二叉搜尋樹的映象樹時,必須按照從後往前的順序在中序遍歷陣列中找和根節點,這樣和根節點鍵值相同的重複結點就會稱為左子樹,這樣才符合二叉搜尋樹的映象樹,如果不這樣的話,樣例二不會通過。

 

C++程式

#include<iostream>
#include<algorithm>

using namespace std;

const int N=1005;

//結點 
struct Node{
	int val;
	Node *l,*r;
	Node(int val):val(val),l(NULL),r(NULL){}
};

//後序遍歷 
bool flag;

void post(Node *x)
{
	if(x)
	{
		post(x->l);
		post(x->r);
		if(flag) printf(" ");
		printf("%d",x->val);
		flag=true;
	}
}

int pre[N],in[N];//前序遍歷和中序遍歷結果 

//是否能構造一棵二叉樹 
bool legal;

//根據前序遍歷和中序遍歷構造二叉搜尋樹
// l,r表示pre的區間,a,b表示in的區間,dir表示從前往後找,還是從後往前找 
Node* build(int l,int r,int a,int b,bool dir)
{
	if(legal&&l<=r)
	{
		int rt=pre[l];
		int mid;
		if(dir)//從前往後找(用於構造二叉搜尋樹) 
		  for(mid=a;mid<=b&&in[mid]!=rt;mid++);
		else//從後往前找(用於構造二叉搜尋樹進行映象操作後的樹) 
		  for(mid=b;mid>=a&&in[mid]!=rt;mid--);
		if((dir&&mid>b)||(!dir&&mid<a))//構造失敗 
		{
			legal=false;
			return NULL;
		} 
		Node *node=new Node(rt);
		node->l=build(l+1,mid-a+l,a,mid-1,dir);
		node->r=build(mid-a+l+1,r,mid+1,b,dir);
		return node; 
	}
	return NULL;
}

//銷燬樹 
void destroy(Node *x)
{
	if(x)
	{
		destroy(x->l);
		destroy(x->r);
		delete x;
		x=NULL;
	}
}

int main()
{
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&pre[i]);
		in[i]=pre[i];
	}
	//二叉搜尋樹的中序遍歷是非遞減的 ,利用前序遍歷和中序遍歷構造二叉樹 
	sort(in,in+n);
	legal=true;
	Node *root=build(0,n-1,0,n-1,true);
	//如果構造失敗,說明不是二叉搜尋樹,進一步判斷它是否為二叉樹進行鏡面操作後的樹 
	if(!legal) 
	{
		destroy(root);//刪除構造失敗的樹 
		reverse(in,in+n);//中序遍歷是非第增的 
		legal=true;
		root=build(0,n-1,0,n-1,false);
	}
	if(legal)//構造二叉搜尋樹或二叉搜尋樹映象後的樹成功 
	{
		printf("YES\n");
		post(root);
	}
	else//失敗 
	  printf("NO\n");
	destroy(root);
	return 0;
}

 

相關文章