1. 程式人生 > >PAT-ADVANCED1043——Is It a Binary Search Tree

PAT-ADVANCED1043——Is It a Binary Search Tree

我的PAT-ADVANCED程式碼倉:https://github.com/617076674/PAT-ADVANCED

原題連結:https://pintia.cn/problem-sets/994805342720868352/problems/994805440976633856

題目描述:

題目翻譯:

1043 這是一棵二分搜尋樹嗎

一棵二分搜尋樹是一棵二叉樹,遞迴地定義如下:

        左子樹的節點都小於當前節點的值。

        右子樹的節點都大於或等於當前節點的值。

        左右子樹都是一棵二分搜尋樹。

如果我們交換左右子樹的每一個節點,交換後的樹稱作二分搜尋樹的映象。

現在給你一串正整數序列,你需要指出這是否是一棵二分搜尋樹的前序遍歷或一棵二分搜尋樹映象的前序遍歷。

輸入格式:

每個輸入檔案包含一個測試用例。在每個測試用例中,第一行給出一個正整數N(<= 1000)。接下來的一行給出N個正整數。一行中的所有數字都由一個空格隔開。

輸出格式:

對每個測試用例,如果該序列是一棵二分搜尋樹的前序遍歷或是一棵二分搜尋樹的映象的前序遍歷,就在第一行中輸出YES。否則,輸出NO。如果答案是YES,則在下一行輸出這棵樹的後序遍歷。一行中的所有數字都由一個空格隔開,行末不得有多餘空格。

輸入樣例1:

7
8 6 5 7 10 8 11

輸出樣例1:

YES
5 7 6 8 11 10 8

輸入樣例2:

7
8 10 11 8 6 7 5

輸出樣例2:

YES
11 8 10 7 5 6 8

輸入樣例3:

7
8 6 8 5 10 9 11

輸出樣例3:

NO

知識點:二分搜尋樹的後序遍歷、前序遍歷、重建二分搜尋樹

思路一:根據前序遍歷結果重建二分搜尋樹及其映象(靜態陣列實現)

對於普通的二叉樹,我們需要中序遍歷 + (前序遍歷/後序遍歷/層序遍歷)才能重建該二叉樹。因為前序遍歷/後序遍歷/層序遍歷的作用都是提供根節點,而中序遍歷的作用是區分左右子樹。關於二叉樹的重建,可以參考我的另一篇博文:

PAT-ADVANCED1020——Tree Traversals,講的是如何根據二叉樹的後序遍歷和中序遍歷重建該二叉樹。

對於二分搜尋樹而言,我們只需根據前序遍歷/後序遍歷/層序遍歷就能重建二分搜尋樹。因為其左右子樹的判定是根據節點的值來比較大小定的,我們只需要有根結點的資訊即可。自然,我們無法通過二分搜尋樹的中序遍歷來重構它。因為任何二分搜尋樹的中序遍歷結果都是一個遞增序列,我們得不到任何根結點的資訊。

對於題給的前序遍歷結果,我們假定這是一棵二分搜尋樹的前序遍歷結果,將其重建為一棵二分搜尋樹,再根據其後序遍歷結果的節點個數是否等於N來判斷題給的序列是否是一棵二分搜尋樹的前序遍歷結果。

同樣的做法,我們再將其重建為一棵二分搜尋樹的映象,再根據其後序遍歷結果的節點個數是否等於N來判斷題給的序列是否是一棵二分搜尋樹的映象的前序遍歷結果。

如果上述兩個判斷都不滿足,則這不是一棵二分搜尋樹的前序遍歷結果,輸出"NO"即可。

時間複雜度和空間複雜度均是O(N)。

C++程式碼:

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

struct node {
	int data;
	int lchild;
	int rchild;
};

int N;	//節點個數
int preOrder[1000];	//前序遍歷
vector<int> postOrder;	//後序遍歷結果
node Node[1000];
int index = 0;

int createBinarySearchTree(int leftPre, int rightPre);	//根據前序遍歷構建二叉搜尋樹
int createMirrorBinarySearchTree(int leftPre, int rightPre);	//根據前序遍歷構建映象二叉搜尋樹
void postOrderTraversal(int root);

int main() {
	cin >> N;
	for(int i = 0; i < N; i++) {
		cin >> preOrder[i];
	}
	int root = createBinarySearchTree(0, N - 1);
	postOrderTraversal(root);
	if(postOrder.size() == N) {
		cout << "YES" << endl;
		for(int i = 0; i < N; i++) {
			cout << postOrder[i];
			if(i != N - 1) {
				cout << " ";
			}
		}
		cout << endl;
	} else {
		postOrder.clear();
		index = 0;
		root = createMirrorBinarySearchTree(0, N - 1);
		postOrderTraversal(root);
		if(postOrder.size() == N) {
			cout << "YES" << endl;
			for(int i = 0; i < N; i++) {
				cout << postOrder[i];
				if(i != N - 1) {
					cout << " ";
				}
			}
			cout << endl;
		} else {
			cout << "NO" << endl;
		}
	}
	return 0;
}

int createBinarySearchTree(int leftPre, int rightPre) {
	if(leftPre > rightPre) {
		return -1;
	}
	int root = index++;
	Node[root].data = preOrder[leftPre];
	int k;
	for(k = leftPre + 1; k <= rightPre; k++) {
		if(preOrder[k] >= preOrder[leftPre]) {
			break;
		}
	}
	int l;
	for(l = k; l <= rightPre; l++) {
		if(preOrder[l] < preOrder[leftPre]) {
			break;
		}
	}
	Node[root].lchild = createBinarySearchTree(leftPre + 1, k - 1);
	Node[root].rchild = createBinarySearchTree(k, l - 1);
	return root;
}

int createMirrorBinarySearchTree(int leftPre, int rightPre) {
	if(leftPre > rightPre) {
		return -1;
	}
	int root = index++;
	Node[root].data = preOrder[leftPre];
	int k;
	for(k = leftPre + 1; k <= rightPre; k++) {
		if(preOrder[k] < preOrder[leftPre]) {
			break;
		}
	}
	int l;
	for(l = k; l <= rightPre; l++) {
		if(preOrder[l] >= preOrder[leftPre]) {
			break;
		}
	}
	Node[root].lchild = createMirrorBinarySearchTree(leftPre + 1, k - 1);
	Node[root].rchild = createMirrorBinarySearchTree(k, l - 1);
	return root;
}

void postOrderTraversal(int root) {
	if(root == -1) {
		return;
	}
	postOrderTraversal(Node[root].lchild);
	postOrderTraversal(Node[root].rchild);
	postOrder.push_back(Node[root].data);
}

C++解題報告:

思路二:根據插入順序重建出二分搜尋樹(靜態陣列實現)

這個思路基於這樣一個事實,給定一棵二分搜尋樹的前序序列。按照該序列向一棵空的二分搜尋樹中依次新增元素,所得到的二分搜尋樹的前序序列一定和給定的前序序列相同。這其實很好理解,因為前序序列先訪問的根結點,再訪問的左右子樹,而我們構建二分搜尋樹時,也是先構建出根節點,再往根節點中依次新增節點。

對於這樣構建出的二分搜尋樹,即使給定的是二分搜尋樹的映象的前序序列,也一定是一棵二分搜尋樹而不會是其映象。對於映象的判斷,我們需要在遍歷這棵二分搜尋樹的時候交換左右子樹的遍歷順序。同理,在求後序遍歷的時候也需要交換左右子樹的遍歷順序。

時間複雜度和空間複雜度均是O(N)。

C++程式碼:

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

struct node {
	int data;
	int lchild;
	int rchild;
};

int N;	//節點個數
vector<int> preOrder;	//前序遍歷
node Node[1000];
int index = 0;
vector<int> preOrderBSTree;
vector<int> preOrderBSTreeMirror;
vector<int> postOrderBSTree;
vector<int> postOrderBSTreeMirror;

int newNode(int v);	//分配一個Node陣列中的節點給新節點,index為其下標
void insert(int &root, int x);	//插入,root為根節點在陣列中的下標,要加引用
int createBinarySearchTree();	//根據輸入的前序遍歷構建二分搜尋樹
void preOrderTraversal(int root);	//二叉搜尋樹前序遍歷
void preOrderTraversalMirror(int root);	//映象二叉搜尋樹前序遍歷
void postOrderTraversal(int root);	//二叉搜尋樹後序遍歷
void postOrderTraversalMirror(int root);	//映象二叉搜尋樹後序遍歷

int main() {
	cin >> N;
	int input;
	for(int i = 0; i < N; i++) {
		cin >> input;
		preOrder.push_back(input);
	}
	int root = createBinarySearchTree();
	preOrderTraversal(root);
	preOrderTraversalMirror(root);
	postOrderTraversal(root);
	postOrderTraversalMirror(root);
	if(preOrder == preOrderBSTree){
		cout << "YES" << endl;
		for(int i = 0; i < postOrderBSTree.size(); i++){
			cout << postOrderBSTree[i];
			if(i != postOrderBSTree.size() - 1){
				cout << " ";
			}
		}
	}else if(preOrder == preOrderBSTreeMirror){
		cout << "YES" << endl;
		for(int i = 0; i < postOrderBSTreeMirror.size(); i++){
			cout << postOrderBSTreeMirror[i];
			if(i != postOrderBSTreeMirror.size() - 1){
				cout << " ";
			}
		}
	}else{
		cout << "NO" << endl;
	}
	return 0;
}

int newNode(int v) {
	Node[index].data = v;
	Node[index].lchild = -1;
	Node[index].rchild = -1;
	return index++;
}

void insert(int &root, int x) {
	if(root == -1) {
		root = newNode(x);
		return;
	}
	if(x < Node[root].data) {
		insert(Node[root].lchild, x);
	} else {
		insert(Node[root].rchild, x);
	}
}

int createBinarySearchTree() {
	int root = -1;	//新建根結點
	for(int i = 0; i < N; i++) {
		insert(root, preOrder[i]);
	}
	return root;
}

void preOrderTraversal(int root) {
	if(root == -1) {
		return;
	}
	preOrderBSTree.push_back(Node[root].data);
	preOrderTraversal(Node[root].lchild);
	preOrderTraversal(Node[root].rchild);
}

void preOrderTraversalMirror(int root) {
	if(root == -1) {
		return;
	}
	preOrderBSTreeMirror.push_back(Node[root].data);
	preOrderTraversalMirror(Node[root].rchild);
	preOrderTraversalMirror(Node[root].lchild);
}

void postOrderTraversal(int root) {
	if(root == -1) {
		return;
	}
	postOrderTraversal(Node[root].lchild);
	postOrderTraversal(Node[root].rchild);
	postOrderBSTree.push_back(Node[root].data);
}

void postOrderTraversalMirror(int root) {
	if(root == -1) {
		return;
	}
	postOrderTraversalMirror(Node[root].rchild);
	postOrderTraversalMirror(Node[root].lchild);
	postOrderBSTreeMirror.push_back(Node[root].data);
}

C++解題報告:

思路三:根據插入順序重建出二分搜尋樹(指標實現)

時間複雜度和空間複雜度均是O(N)。

C++程式碼:

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

struct node {
	int data;
	node* lchild;
	node* rchild;
};

int N;	//節點個數
vector<int> preOrder;	//前序遍歷
vector<int> preOrderBSTree;
vector<int> preOrderBSTreeMirror;
vector<int> postOrderBSTree;
vector<int> postOrderBSTreeMirror;

node* newNode(int v);	//分配一個Node陣列中的節點給新節點,index為其下標
void insert(node* &root, int x);	//插入,root為根節點在陣列中的下標,要加引用
node* createBinarySearchTree();	//根據輸入的前序遍歷構建二分搜尋樹
void preOrderTraversal(node* root);	//二叉搜尋樹前序遍歷
void preOrderTraversalMirror(node* root);	//映象二叉搜尋樹前序遍歷
void postOrderTraversal(node* root);	//二叉搜尋樹後序遍歷
void postOrderTraversalMirror(node* root);	//映象二叉搜尋樹後序遍歷

int main() {
	cin >> N;
	int input;
	for(int i = 0; i < N; i++) {
		cin >> input;
		preOrder.push_back(input);
	}
	node* root = createBinarySearchTree();
	preOrderTraversal(root);
	preOrderTraversalMirror(root);
	postOrderTraversal(root);
	postOrderTraversalMirror(root);
	if(preOrder == preOrderBSTree){
		cout << "YES" << endl;
		for(int i = 0; i < postOrderBSTree.size(); i++){
			cout << postOrderBSTree[i];
			if(i != postOrderBSTree.size() - 1){
				cout << " ";
			}
		}
	}else if(preOrder == preOrderBSTreeMirror){
		cout << "YES" << endl;
		for(int i = 0; i < postOrderBSTreeMirror.size(); i++){
			cout << postOrderBSTreeMirror[i];
			if(i != postOrderBSTreeMirror.size() - 1){
				cout << " ";
			}
		}
	}else{
		cout << "NO" << endl;
	}
	return 0;
}

node* newNode(int v) {
	node* root = new node;
	root->data = v;
	root->lchild = NULL;
	root->rchild = NULL;
	return root;
}

void insert(node* &root, int x) {
	if(root == NULL) {
		root = newNode(x);
		return;
	}
	if(x < root->data) {
		insert(root->lchild, x);
	} else {
		insert(root->rchild, x);
	}
}

node* createBinarySearchTree() {
	node* root = NULL;	//新建根結點
	for(int i = 0; i < N; i++) {
		insert(root, preOrder[i]);
	}
	return root;
}

void preOrderTraversal(node* root) {
	if(root == NULL) {
		return;
	}
	preOrderBSTree.push_back(root->data);
	preOrderTraversal(root->lchild);
	preOrderTraversal(root->rchild);
}

void preOrderTraversalMirror(node* root) {
	if(root == NULL) {
		return;
	}
	preOrderBSTreeMirror.push_back(root->data);
	preOrderTraversalMirror(root->rchild);
	preOrderTraversalMirror(root->lchild);
}

void postOrderTraversal(node* root) {
	if(root == NULL) {
		return;
	}
	postOrderTraversal(root->lchild);
	postOrderTraversal(root->rchild);
	postOrderBSTree.push_back(root->data);
}

void postOrderTraversalMirror(node* root) {
	if(root == NULL) {
		return;
	}
	postOrderTraversalMirror(root->rchild);
	postOrderTraversalMirror(root->lchild);
	postOrderBSTreeMirror.push_back(root->data);
}

C++解題報告: