PAT A1143 Lowest Common Ancestor (30 分)

The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U and V as descendants.

A binary search tree (BST) is recursively defined as a binary tree which has the following properties:

The left subtree of a node contains only nodes with keys less than the node's key.
The right subtree of a node contains only nodes with keys greater than or equal to the node's key.
Both the left and right subtrees must also be binary search trees.

Given any two nodes in a BST, you are supposed to find their LCA.

Input Specification:

Each input file contains one test case. For each case, the first line gives two positive integers: M (≤ 1,000), the number of pairs of nodes to be tested; and N (≤ 10,000), the number of keys in the BST, respectively. In the second line, N distinct integers are given as the preorder traversal sequence of the BST. Then M lines follow, each contains a pair of integer keys U and V. All the keys are in the range of int.
Output Specification:

For each given pair of U and V, print in a line LCA of U and V is A. if the LCA is found and A is the key. But if A is one of U and V, print X is an ancestor of Y. where X is A and Y is the other node. If U or V is not found in the BST, print in a line ERROR: U is not found. or ERROR: V is not found. or ERROR: U and V are not found..

Sample Input:

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

Sample Output:

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.

思路:

一开始没有考虑时间复杂度,数据量一共是10^7次方级别的数据,可惜题目只给了200ms,所以一开始自己直接上的递归的方式,可惜只有18分,有三个点超时,然后仔细查看代码发现递归代码写的没有错误也并不会出现死循环,就又开始思考是不是没有剪枝,但是也没法剪,所以肯定是方法用错了,一般只给200ms的题目,大多数都有巧妙的做法而不是递归上手,但是还是有必要去掌握递归求最近公共祖先的解法,毕竟pat就喜欢这么玩,例如堆这个考点考了堆排,还考了两次建堆,都是换着方法去考你,下次指不定就出一个非二叉树求最近公共祖先,haha~
首先我们知道先序遍历的顺序是TLR(根左右),又因为BST的中序是一个有序序列,所以可以知道要查询的两个结点ab的父节点的值一定大于等于min(a,b)并且小于等于max(a,b),在BST的先序序列中第一个满足的结点即为结果。

实现代码:

方法一:

非递归版  适用于BST求公共最近祖先
	
#include <iostream>
#include <unordered_map>
#include <cmath>
using namespace std;
const int N=10010;
int fid[N];//用来保存父节点

int main() {
	int n,m,val;
	cin>>n>>m;
	unordered_map<int,int> mp;
	for(int i=0; i<m; i++) {
		scanf("%d",&val);
		fid[i]=val;
		mp[val]=1;
	}
	while(n--) {
		int d1,d2,ans=-1;
		scanf("%d%d",&d1,&d2);
		for(int i=0; i<m; i++) {
			if(fid[i]>=min(d1,d2)&&fid[i]<=max(d1,d2)) {
				ans=fid[i];
				break;
			}
		}
		if(mp.count(d1)==0&&mp.count(d2)==0) printf("ERROR: %d and %d are not found.",d1,d2);
		else if(mp.count(d1)==0||mp.count(d2)==0)printf("ERROR: %d is not found.",mp.count(d1)==0?d1:d2);
		else if(ans==d1||ans==d2) printf("%d is an ancestor of %d.",ans==d1?d1:d2,ans==d1?d2:d1);
		else printf("LCA of %d and %d is %d.",d1,d2,ans);
		cout<<endl;
	}
	return 0;
}

方法二:

递归版本  本题会超时 但是适用于任何二叉树求最近公共祖先
	
#include <iostream>
#include <unordered_map>
using namespace std;
const int N=10010;
int n,m,in[N],pre[N];
unordered_map<int,int> mp;
struct node {
	int data;
	node *l,*r;
};


node* build(int preL,int preR,int inL,int inR) {
	if(preL>preR) return NULL;
	node *root=new node;
	root->data=pre[preL];
	mp[root->data]=1;
	int k;
	for(k=inL; k<=inR; k++) {
		if(in[k]==root->data) break;
	}
	int leftNodeNum=k-inL;
	root->l=build(preL+1,preL+leftNodeNum,inL,k-1);
	root->r=build(preL+leftNodeNum+1,preR,k+1,inR);
	return root;
}

int LCA(node *root,int p,int q) {
	if(root==NULL) return -1;
	int l=LCA(root->l,p,q);
	int r=LCA(root->r,p,q);
	if(root->data==p||root->data==q) return root->data==p?p:q;//p或q是另一个结点祖先结点情况
	if(l!=-1&&r!=-1) return root->data;//pq分处在当前结点左右子树的情况
	return l!=-1?l:r;//哪棵子树找到祖先结点了就返回哪个结果
}

int main() {
	cin>>m>>n;
	for(int i=0; i<n; i++) scanf("%d",&in[i]);
	for(int i=0; i<n; i++) scanf("%d",&pre[i]);
	node *root=build(0,n-1,0,n-1);
	while(m--) {
		int a,b;
		scanf("%d%d",&a,&b);
		if(mp.count(a)==0||mp.count(b)==0) {
			if(mp.count(a)==0&&mp.count(b)==0) printf("ERROR: %d and %d are not found.",a,b);
			else if(mp.count(a)==0) printf("ERROR: %d is not found.",a);
			else printf("ERROR: %d is not found.",b);
		} else {
			int ans=LCA(root,a,b);
			if(ans==a||ans==b) printf("%d is an ancestor of %d.",ans==a?a:b,ans==a?b:a);
			else printf("LCA of %d and %d is %d.",a,b,ans);
		}
		printf("\n");
	}
	return 0;
}

小结:
今年学习的收获是在于,做题更少地功利性去做了,认为做对即可,而是想着一题掌握更多技巧思路作为自己学习的出发点,我觉得之前考研失败就是自己太功利了,我还在努力中,或许茫茫人海有少数同学能看到我的这篇文章,请你足够相信,你的付出终有回报,你多花的时间一定是有意义的。

posted @ 2021-02-02 22:51  coderJ_ONE  阅读(79)  评论(0)    收藏  举报