1151 LCA in a Binary Tree (30分) 树、LCA、前序中序确定树。两种思路。

题目链接
1151 LCA in a Binary Tree (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.

Given any two nodes in a binary tree, 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 binary tree, respectively. In each of the following two lines, N distinct integers are given as the inorder and preorder traversal sequences of the binary tree, respectively. It is guaranteed that the binary tree can be uniquely determined by the input sequences. 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 binary tree, 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
7 2 3 4 6 5 1 8
5 3 7 2 6 4 8 1
2 6
8 1
7 9
12 -3
0 8
99 99
Sample Output:
LCA of 2 and 6 is 3.
8 is an ancestor of 1.
ERROR: 9 is not found.
ERROR: 12 and -3 are not found.
ERROR: 0 is not found.
ERROR: 99 and 99 are not found.

思路一:

1.不用建树。
2.根据前序中序,可以划分树的层次,找出根与左右子树,递归。在对树剖析时,加入LCA算法:
3.若a,b在当前根的左侧,则往左子树递归, 若在右侧往右递归。若一左一右,则当前根即为a与b的LCA。 当然,若a、b中有一个就是当前的根,那么这个也就是要找的LCA。
4.优化1: 可以建一个map在节点输入时就记录下每个节点在先序in中的编号, 这样每一次在找根、a和b的位置时,不用从头枚举,直接用map映射,简化代码,优化复杂度。
5.优化2: void dfs(inL, inR, preL, preR) 这种根据中序前序确定树的递归函数中,往往这4个参数并不是都需要用到, 这里preR其实用不到,故可以省去这个参数。(当然也可以选择省去inR) inL, inR递归出口判断,不能少;preL找出当前根,必不可少。

Ac代码1:


#include<cstdio>
#include<map>
#include<vector>
using namespace std;
map<int , int >pos;
vector<int> in, pre;
void dfs(int inL, int inR, int preL, int a, int b){
	if(inL > inR)
		return;
	int r = pre[preL];
	int k = pos[r], aIn = pos[a], bIn = pos[b];
	if((aIn < k && bIn > k) || (aIn > k && bIn < k))
		printf("LCA of %d and %d is %d.\n", a, b, r );
	else if(k > aIn && k > bIn )
		dfs(inL, k-1, preL+1, a, b);
	else if(k < aIn && k < bIn)
		dfs(k+1, inR, preL+k-inL+1, a, b);
	else if(k == aIn)
		printf("%d is an ancestor of %d.\n",a, b);
	else if(k == bIn)
		printf("%d is an ancestor of %d.\n",b, a );

}
int main(){
	int m, n, a, b;
	scanf("%d %d", &m, &n);
	in.resize(n+1), pre.resize(n+1);
	for(int i = 1; i<=n; i++){
		scanf("%d", &in[i]);
		pos[in[i]] = i;
	}
	for(int i = 1; i <= n; i++)
		scanf("%d", &pre[i]);
	while(m--){
		scanf("%d %d", &a, &b);
		if(pos[a] == 0 && pos[b] == 0)
			printf("ERROR: %d and %d are not found.\n",a, b );
		else if(pos[a] == 0 || pos[b] == 0)
			printf("ERROR: %d is not found.\n",pos[a] == 0 ? a : b );
		else dfs(1, n, 1,a, b);
	}
	return 0;
}

思路二:

1.也不建树。
2.记录每一个节点的直接父节点。由于题目没限定给的节点值是1 ~ n 或 0 ~ n-1的连续值,故不用数组记录,改用一个map<int, int>做映射。在dfs(inL,inR,preL,ancestor)这一递归过程中去记录每个节点的父节点。
同时再建一个map<int, int> exist用来在输入节点的过程中标记节点存在。
3.对于一对a,b:先根据exist 判断其是否存在,若a , b都存在,则从自身开始,导出其父节点,直到根节点为止,导出到路径p1,p2. 然后根据p1,p2找到他们最近的祖先节点即可。
总结: 这个办法没有上面的办法那么灵性, 思路一巧妙的递归;思路二笨办法,老老实实导出路径, 再比对。

Ac代码2

#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
vector<int> in, pre;
map<int, int> exist, father;
int root;

void dfs(int inL, int inR, int preL, int preR, int ancestor){
	if(inL > inR) return ;
	int root = pre[preL];
	father[root] = ancestor;
	// printf("test: father[%d] = %d\n",root, ancestor );
	int k = inL;
	while(in[k] != root) k++;
	dfs(inL, k-1, preL+1, preL+(k - inL), root);
	dfs(k+1, inR, preL+ (k - inL) +1, preR, root);

}
void getpath(int x, vector<int> &path){
	vector<int> temppath;
	while(x != root){
		temppath.push_back(x);
		x = father[x];
	}
	temppath.push_back(root);
	path = temppath;
	reverse(path.begin(), path.end());
	// for(int i = 0; i < path.size(); i++)printf("%d ",path[i] );
	// printf("\n");
}
int main(){
	int m, n;
	scanf("%d %d", &m, &n);
	in.resize(n), pre.resize(n);
	for(int i = 0; i < n; i++){
		scanf("%d", &in[i]);
		exist[in[i]] = 1;
	}
	for(int i = 0; i < n; i++){
		scanf("%d", &pre[i]);
	}
	dfs(0, n-1, 0, n-1, 999999999);
	root = pre[0];
	while(m--){
		int a, b;
		scanf("%d %d", &a, &b);
		if(exist[a] == 0 && exist[b] == 0){
			printf("ERROR: %d and %d are not found.\n",a, b );
		}
		else if(exist[a] == 0){
			printf("ERROR: %d is not found.\n",a );
		}
		else if(exist[b] == 0){
			printf("ERROR: %d is not found.\n",b );
		}
		else{
			vector<int>p1, p2;
			getpath(a, p1);
			getpath(b, p2);
			int i = 0;
			while(i+1 < min(p1.size(), p2.size()) && p1[i+1] == p2[i+1]) i++;
			int ans = p1[i];
			if(ans != a && ans != b){
				printf("LCA of %d and %d is %d.\n",a, b, ans );
			}
			else if(ans == a)
				printf("%d is an ancestor of %d.\n",ans, b );
			else printf("%d is an ancestor of %d.\n",ans, a );
		}
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值