1. 程式人生 > >7-4 是否同一棵二叉搜尋樹-PTA

7-4 是否同一棵二叉搜尋樹-PTA

       給定一個插入序列就可以唯一確定一棵二叉搜尋樹。然而,一棵給定的二叉搜尋樹卻可以由多種不同的插入序列得到。例如分別按照序列{2, 1, 3}和{2, 3, 1}插入初始為空的二叉搜尋樹,都得到一樣的結果。於是對於輸入的各種插入序列,你需要判斷它們是否能生成一樣的二叉搜尋樹。

輸入格式:

      輸入包含若干組測試資料。每組資料的第1行給出兩個正整數N (≤10)和L,分別是每個序列插入元素的個數和需要檢查的序列個數。第2行給出N個以空格分隔的正整數,作為初始插入序列。最後L行,每行給出N個插入的元素,屬於L個需要檢查的序列。

     簡單起見,我們保證每個插入序列都是1到N的一個排列。當讀到N為0時,標誌輸入結束,這組資料不要處理。

輸出格式:

     對每一組需要檢查的序列,如果其生成的二叉搜尋樹跟對應的初始序列生成的一樣,輸出“Yes”,否則輸出“No”。

輸入樣例:

4 2
3 1 4 2
3 4 1 2
3 2 4 1
2 1
2 1
1 2
0

輸出樣例:

Yes
No
No

 

       根據題目意思可得,我們需要判斷兩個序列是否對應相同的二叉搜尋樹,求解思路有多種,例如:

1.我們可以分別建立兩個二叉搜尋樹進行判別,分別遞迴判斷其左右子樹是否相同;

2.不建樹的判別方法;比如對於序列3 1 4 2和3 2 4 1,我們首先比較第一個數字(根結點),若不相同,則肯定不是相同的二叉樹;其次把序列1和2中比根結點小的數放在根結點左邊,大的放在右邊,則序列變為1 2 3 4和2 1 3 4;然後分別比較根結點左邊序列和右邊序列,看是否相同。

3.建立一棵樹,在判別其他序列是否與該樹一致。

具體可見老師講的視訊https://www.icourse163.org/learn/ZJU-93001?tid=1003013004#/learn/content?type=detail&id=1004242208&cid=1005239479

      我們這裡主要講述第3種方法。因此我們的程式框架為:

int main()
{
 對每組資料
    *讀入N和L;
    *根據第一行序列建立數T;
    *依據樹T分別判別後面的L個序列是否能與樹T形成同一個二叉搜尋樹並輸出結果;
 return 0;
}

        那麼我們首先需要解決的便是如何建立搜尋樹T 。我們可以利用二叉搜尋樹的插入演算法,如下:

/**********二叉搜尋樹插入演算法***********/
BinTree Insert(int X,BinTree BST)
{
	if( !BST ){  //空樹 
		BST = (BinTree)malloc(sizeof(struct TreeNode));
		BST->Data = X;
		BST->flag = 0;
		BST->Left = BST->Right = NULL;
	}else{
		if(X > BST->Data)  
		    BST->Right = Insert(X,BST->Right);  //遞迴插入右子樹 
		else if(X < BST->Data)  
		    BST->Left = Insert(X,BST->Left);    //遞迴插入左子樹  
		//else X 存在,什麼也不做 
	}
	return BST; 
}

       我們只需要連續插入N次,就可以構成一個搜尋樹了。

BinTree MakeTree(int N)
{
	int x;
	BinTree BST = NULL;
	
	for(int i=0; i<N; i++){
		scanf("%d",&x);
		BST = Insert(x,BST);  //插入法構建二叉搜尋樹 
	}
	
	return BST;
}

      其次,我們如何判別序列與樹T是否一致呢?方法如下:

       我們如何表徵結點在前面出現過呢?只需要給結點的資料結構里加上一個flag標誌,flag=1說明前面出現過(訪問過),否則則相反。

typedef struct TreeNode *Position;
typedef Position BinTree; /* 二叉樹型別 */
struct TreeNode{ /* 樹結點定義 */
    int Data; /* 結點資料 */
    BinTree Left;     /* 指向左子樹 */
    BinTree Right;    /* 指向右子樹 */
    int flag;  //用於標記此結點是否被訪問過 
};

       判別函式如下:

int Check(BinTree BST,int X)
{
	if( BST->flag ){  /*該結點前面已經訪問過(見過)*/
		if(X < BST->Data)  return Check(BST->Left,X);
		else if(X > BST->Data)  return Check(BST->Right,X);
		else return 0;  //序列中有兩個相同的數,那麼其肯定不與二叉搜尋樹T相同,返回0 
	}else{  /*沒見過*/ 
		if(X == BST->Data){  
			BST->flag = 1; //標記為訪問過(見過) 
			return 1;  //一致 
		}else{
			return 0;  //不一致 
		}
	} 
 } 

int Judge(BinTree BST,int N)
{
	int x;
	int flag = 0;  //flag=0代表目前還一致, 1代表已經不一致 
	
	scanf("%d",&x);
	if(x != BST->Data)  flag = 1;  //若輸入序列的第一個數與根節點不一致,則不是同一棵二叉搜尋樹 
	else BST->flag = 1;   //否則,此結點已被訪問過,標記記錄為1 
	for(int i=1; i<N; i++){
		scanf("%d",&x);
		/*如果flag已經為1,則不需要繼續做Check()函式,繼續讀取完此序列的數*/
		/*否則,判斷Check()函式,若為0,flag = 1*/ 
		if((!flag) && (!Check(BST,x)))  flag = 1; 
	}
	if( flag )  return 0;  //不是同一棵樹 
	else return 1; 
}

 

      總的實現程式為: 

#include<stdio.h>
#include<stdlib.h>

typedef struct TreeNode *Position;
typedef Position BinTree; /* 二叉樹型別 */
struct TreeNode{ /* 樹結點定義 */
    int Data; /* 結點資料 */
    BinTree Left;     /* 指向左子樹 */
    BinTree Right;    /* 指向右子樹 */
    int flag;  //用於標記此結點是否被訪問過 
};

Position MakeTree(int N);
int Judge(BinTree BST,int N);
void ResetTree(BinTree BST);
void FreeTree(BinTree BST);

int main()
{
	int N,L;
	BinTree T = NULL;
	
	scanf("%d",&N);
	while( N ){
		scanf("%d",&L);
		T = MakeTree(N);
		for(int i=0; i<L; i++){
			if(Judge(T,N))  printf("Yes\n");
			else  printf("No\n");
			ResetTree(T);  //清除樹中每個結點的flag標記 
		}
		FreeTree(T);   //釋放樹的結點空間 
		scanf("%d",&N);
	}
	return 0;
}

/**********二叉搜尋樹插入演算法***********/
Position Insert(int X,BinTree BST)
{
	if( !BST ){  //空樹 
		BST = (BinTree)malloc(sizeof(struct TreeNode));
		BST->Data = X;
		BST->flag = 0;
		BST->Left = BST->Right = NULL;
	}else{
		if(X > BST->Data)  
		    BST->Right = Insert(X,BST->Right);  //遞迴插入右子樹 
		else if(X < BST->Data)  
		    BST->Left = Insert(X,BST->Left);    //遞迴插入左子樹  
		//else X 存在,什麼也不做 
	}
	return BST; 
}

Position MakeTree(int N)
{
	int x;
	BinTree BST = NULL;
	
	for(int i=0; i<N; i++){
		scanf("%d",&x);
		BST = Insert(x,BST);  //插入法構建二叉搜尋樹 
	}
	
	return BST;
}

int Check(BinTree BST,int X)
{
	if( BST->flag ){  /*該結點前面已經訪問過(見過)*/
		if(X < BST->Data)  return Check(BST->Left,X);
		else if(X > BST->Data)  return Check(BST->Right,X);
		else return 0;  //序列中有兩個相同的數,那麼其肯定不與二叉搜尋樹T相同,返回0 
	}else{  /*沒見過*/ 
		if(X == BST->Data){  
			BST->flag = 1; //標記為訪問過(見過) 
			return 1;  //一致 
		}else{
			return 0;  //不一致 
		}
	} 
 } 

int Judge(BinTree BST,int N)
{
	int x;
	int flag = 0;  //flag=0代表目前還一致, 1代表已經不一致 
	
	scanf("%d",&x);
	if(x != BST->Data)  flag = 1;  //若輸入序列的第一個數與根節點不一致,則不是同一棵二叉搜尋樹 
	else BST->flag = 1;   //否則,此結點已被訪問過,標記記錄為1 
	for(int i=1; i<N; i++){
		scanf("%d",&x);
		/*如果flag已經為1,則不需要繼續做Check()函式,繼續讀取完此序列的數*/
		/*否則,判斷Check()函式,若為0,flag = 1*/ 
		if((!flag) && (!Check(BST,x)))  flag = 1; 
	}
	if( flag )  return 0;  //不是同一棵樹 
	else return 1; 
}

void ResetTree(BinTree BST)
{
	if( BST ){
	    if(BST->Left) ResetTree(BST->Left);   //清除左子樹標記 
	    if(BST->Right) ResetTree(BST->Right);  //清除右子樹標記 
	    BST->flag = 0;          //清除根節點標記 
	}
}

void FreeTree(BinTree BST)
{
	if( BST ){
	    if(BST->Left) FreeTree(BST->Left);    //釋放左子樹空間 
	    if(BST->Right) FreeTree(BST->Right);   //釋放右子樹空間 
	    free(BST);              //釋放根節點空間 
	}
}