PAT-ADVANCED1143——Lowest Common Ancestor

我的PAT-ADVANCED代码仓:https://github.com/617076674/PAT-ADVANCED

原题链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805343727501312

题目描述:

题目翻译:

1143 最近公共祖先

树中两个节点U和V的最低共同祖先(LCA)是具有U和V作为后代的最深节点。

二叉搜索树(BST)以递归方式定义为具有以下属性的二叉树:

        节点的左子树仅包含键小于节点键的节点。

        节点的右子树仅包含键大于或等于节点键的节点。

        左右子树也必须是二叉搜索树。

给定BST中的任何两个节点,你需要找到他们的LCA。

输入格式:

每个输入文件包含一个测试用例。对每个测试用例,第一行给出两个正整数:M(<= 1000),代表要测试的节点对的数量;和N(<= 10000),代表BST中的键数。在第二行中,给出N个不同的整数作为BST的前序遍历序列。然后是M行,每行包含一对整数键U和V。所有键都在int范围内。

输出格式:

对任意给定的一组U和V,如果LCA找着了,且其值为A,则在一行中输出“LCA of U and V is A.”。但是如果A和U或V相等,输出“X is an ancestor of Y.”,其中X是A,Y是另一个节点。如果U或V没有在树中找到,则输出“ERROR: U is not found.”或“ERROR: V is not found.”。如果U和V都没有在树中找到,则输出“ERROR: U and V are not found.”。

输入样例:

6 8
6 3 1 2 5 4 8 7
2 5
8 7
1 9
12 -3
0 8
99 99

输出样例:

LCA of 2 and 5 is 3.
8 is an ancestor of 7.
ERROR: 9 is not found.
ERROR: 12 and -3 are not found.
ERROR: 0 is not found.
ERROR: 99 and 99 are not found.

知识点:递归、二分查找法

思路一:由前序遍历确定根结点,中序遍历区分左右子树,递归地寻找最近公共祖先

一开始我的思路根据前序遍历的插入顺序来重建出一棵二分搜索树,但这样做太耗时间了,会导致测试点2,3,4,5均超时。事实上我们完全没必要重建该二叉树。单单根据二分搜索树的前序遍历我们就可以区分其根节点和左右子树了。

递归函数的定义:int findAncestor(int preLeft, int preRight, int inLeft, int inRight, int num1, int num2)

在中序遍历[inLeft, inRight]范围内寻找值为preOrder[preLeft]的索引时,考虑到中序遍历是一个递增的序列,需要用二分查找法加快速度,否则无法通过测试点4。

递归的过程其实很简单,如果num1和num2均小于当前根结点,则在左子树中寻找。如果num1和num2均大于当前根结点,则在右子树中寻找。否则,当前根结点就是num1和num2的最近公共祖先。

C++代码:

#include<iostream>
#include<vector>
#include<set>

using namespace std;

vector<int> preOrder;
vector<int> inOrder;
int num1, num2;

int findAncestor(int preLeft, int preRight, int inLeft, int inRight, int num1, int num2);

int main() {
	int M, N;
	scanf("%d %d", &M, &N);
	int num;
	set<int> numSet;
	for(int i = 0; i < N; i++) {
		scanf("%d", &num);
		preOrder.push_back(num);
		numSet.insert(num);
	}
	for(set<int>::iterator it = numSet.begin(); it != numSet.end(); it++){
		inOrder.push_back(*it);
	}
	set<int>::iterator it1, it2;
	for(int i = 0; i < M; i++) {
		scanf("%d %d", &num1, &num2);
		it1 = numSet.find(num1);
		it2 = numSet.find(num2);
		if(it1 != numSet.end() && it2 != numSet.end()) {
			int ancestor = findAncestor(0, N - 1, 0, N - 1, num1, num2);
			if(ancestor == num1){
				printf("%d is an ancestor of %d.\n", ancestor, num2);
			}else if(ancestor == num2){
				printf("%d is an ancestor of %d.\n", ancestor, num1);
			}else{
				printf("LCA of %d and %d is %d.\n", num1, num2, ancestor);
			}
		} else if(it1 == numSet.end() && it2 != numSet.end()) {
			printf("ERROR: %d is not found.\n", num1);
		} else if(it1 != numSet.end() && it2 == numSet.end()) {
			printf("ERROR: %d is not found.\n", num2);
		} else {
			printf("ERROR: %d and %d are not found.\n", num1, num2);
		}
	}
	return 0;
}

int findAncestor(int preLeft, int preRight, int inLeft, int inRight, int num1, int num2) {
	int k;
	int left = inLeft, right = inRight;	//二分查找法 
	while(left <= right){
		int mid = left + (right - left) / 2;
		if(inOrder[mid] == preOrder[preLeft]){
			k = mid;
			break;
		}else if(inOrder[mid] < preOrder[preLeft]){
			left = mid + 1;
		}else{
			right = mid - 1;
		}
	}
	int numLeft = k - inLeft; 
	if(num1 < preOrder[preLeft] && num2 < preOrder[preLeft]){
		return findAncestor(preLeft + 1, preLeft + numLeft, inLeft, k - 1, num1, num2);
	}else if(num1 > preOrder[preLeft] && num2 > preOrder[preLeft]){
		return findAncestor(preLeft + numLeft + 1, preRight, k + 1, inRight, num1, num2);
	}else{
		return preOrder[preLeft];
	}
}

C++解题报告:

思路二:只遍历一次前序遍历解决

用set存储前序遍历出现过的值。

对于每一组查询的U和V,判断其是否在set集合里,来判断其是否存在在树中。

对于U和V都存在的情况,依次遍历前序遍历结果,寻找到在U和V之间的那个值,这个值就是所要寻找的最近公共祖先。

C++代码:

#include<iostream>
#include<set>

using namespace std;

int M, N;
set<int> numSet;
int num1, num2;

int main() {
	scanf("%d %d", &M, &N);
	int nums[N];
	for(int i = 0; i < N; i++) {
		scanf("%d", &nums[i]);
		numSet.insert(nums[i]);
	}
	for(int i = 0; i < M; i++) {
		scanf("%d %d", &num1, &num2);
		set<int>::iterator it1 = numSet.find(num1);
		set<int>::iterator it2 = numSet.find(num2);
		if(it1 != numSet.end() && it2 != numSet.end()) {
			for(int j = 0; j < N; j++) {
				if((nums[j] >= num1 && nums[j] <= num2) || (nums[j] >= num2 && nums[j] <= num1)) {
					if(num1 != nums[j] && num2 != nums[j]) {
						printf("LCA of %d and %d is %d.\n", num1, num2, nums[j]);
					}else if(num1 == nums[j]){
						printf("%d is an ancestor of %d.\n", num1, num2);
					}else if(num2 == nums[j]){
						printf("%d is an ancestor of %d.\n", num2, num1);
					}
					break;
				}
			}
		} else if(it1 == numSet.end() && it2 != numSet.end()) {
			printf("ERROR: %d is not found.\n", num1);
		} else if(it1 != numSet.end() && it2 == numSet.end()) {
			printf("ERROR: %d is not found.\n", num2);
		} else {
			printf("ERROR: %d and %d are not found.\n", num1, num2);
		}
	}
}

C++解题报告:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值