1151 LCA in a Binary Tree (30 point(s))

本文详细解析了PAT考试中寻找二叉树最低公共祖先(LCA)的问题,通过先序和中序遍历构建二叉树,并实现LCA算法。文章包括输入输出规范、示例及完整代码实现。

题目描述

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.寻找最小公共祖先节点

  • 构建儿叉树
    这里主要考察先序遍历以及中序遍历来构建二叉树,这种构建方法在PAT中非常常见,是需要记住的。
  • 寻找最小公共祖先节点。具体见代码注释。
#include <set>
#include <map>
#include <iostream>
#include <vector>
using namespace std;
int n, m;
int v1, v2;
vector<int> inorder;//中序
vector<int> preorder;//先序
map<int, bool> flags;
struct tree_node//树节点的结构体
{
	int val;
	tree_node* left;
	tree_node* right;
};
tree_node* build_tree(int in_l, int in_r, int pre_l, int pre_r) {
//以上函数的参数分别代表中序左边序号,中序右边序号,先序左边序号,先序右边序号
	if (pre_l > pre_r) return NULL;
	int root = preorder[pre_l];//先序序列的最左边节点就是他的根节点
	int root_index;
	for (int i = in_l; i <= in_r; i++) {//寻找到中序序列中的根节点的位置
		if (inorder[i] == root) {
			root_index = i;
			break;
		}
	}
	int length = root_index - in_l;// 计算出左子树的个数
	tree_node* node = new tree_node;
	node->val = root;
	node->left = build_tree(in_l, root_index - 1, pre_l + 1, pre_l + length);//计算出左子树在先序序列和中序序列中的边界位置。
	node->right = build_tree(root_index + 1, in_r, pre_l + length + 1, pre_r);
	return node;
}
// 查找最小公共祖先节点
tree_node* ldr_node = NULL;
tree_node* find_ldr(tree_node* ptr) {
	if (ptr == NULL)	return NULL;//遇到叶子节点就返回
	if (ptr->val == v1 || ptr->val == v2) return ptr;//返回找到的节点值
	tree_node* left = find_ldr(ptr->left);
	tree_node* right = find_ldr(ptr->right);
	if (left && right) return ptr;//如果左子树和右子树同时找到那么这个节点就是我们要找的节点
	return left == NULL ? right : left;
}
int main() {
	cin >> m >> n;
	inorder.resize(n);
	preorder.resize(n);
	for (int i = 0; i < n; i++) {
		cin >> inorder[i];
		flags[inorder[i]] = 1;
	}
	for (int i = 0; i < n; i++) {
		cin >> preorder[i];
	}
	tree_node* root = build_tree(0, n - 1, 0, n - 1);
	for (int ii = 0; ii < m; ii++) {
		cin >> v1 >> v2;
		if (flags[v1] == 0 && flags[v2] == 0) {
			printf("ERROR: %d and %d are not found.\n", v1, v2);
			continue;
		}
		else if (flags[v1] == 0) {
			printf("ERROR: %d is not found.\n", v1);
			continue;
		}
		else if (flags[v2] == 0) {
			printf("ERROR: %d is not found.\n", v2);
			continue;
		}
		ldr_node = find_ldr(root);
		if (ldr_node->val == v1) {
			printf("%d is an ancestor of %d.\n", v1, v2);
		}
		else if (ldr_node->val == v2) {
			printf("%d is an ancestor of %d.\n", v2, v1);
		}
		else {
			printf("LCA of %d and %d is %d.\n", v1, v2, ldr_node->val);
		}
	}
}
//#pragma GCC optimize(2,3,“Ofast”,“inline”, “-ffast-math”) //#pragma GCC target(“avx,sse2,sse3,sse4,mmx”) #include #include #include #include #include #include #include #include #include<unordered_map> #include #include #include #include #include #include #include #define fi first #define se second #define pb push_back #define y1 hsduaishxu #define mkp make_pair using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; typedef pair<int,int> pii; typedef pair<ll,int> pli; typedef pair<int,ll> pil; typedef pair<ll,ll> pll; typedef unsigned int uint; typedef vector vpii; typedef int128 i128; const int maxn=1000005; const ll mod=1000000007; inline int Min(int x,int y){return x<y?x:y;} inline int Max(int x,int y){return x>y?x:y;} inline ll Min(ll x,ll y){return x<y?x:y;} inline ll Max(ll x,ll y){return x>y?x:y;} inline void ad(int &x,int y,int z){x=y+z;if(x>=mod) x-=mod;} inline void ad(ll &x,ll y,ll z){x=y+z;if(x>=mod) x-=mod;} inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;} inline void ad(int &x,ll y){x+=y;if(x>=mod) x-=mod;} inline void ad(ll &x,ll y){x+=y;if(x>=mod) x-=mod;} inline void siu(int &x,int y,int z){x=y-z;if(x<0) x+=mod;} inline void siu(int &x,int y){x-=y;if(x<0) x+=mod;} inline void siu(ll &x,ll y){x-=y;if(x<0) x+=mod;} inline ll myabs(ll x){return x<0?-x:x;} inline void tmn(int &x,int y){if(y<x) x=y;} inline void tmx(int &x,int y){if(y>x) x=y;} inline void tmn(ll &x,ll y){if(y<x) x=y;} inline void tmx(ll &x,ll y){if(y>x) x=y;} ll qpow(ll aa,ll bb){ll res=1;while(bb){if(bb&1) res=resaa%mod;aa=aaaa%mod;bb>>=1;}return res;} ll qpow(ll aa,ll bb,ll md){ll res=1;while(bb){if(bb&1) res=(i128)resaa%md;aa=(i128)aaaa%md;bb>>=1;}return res;} inline ll Inv(ll x,ll md){return qpow(x,md-2,md);} inline ll Inv(ll x){return qpow(x,mod-2);} int ,; int n,k; int p[maxn],q[maxn]; ll ans; vector g[maxn],h[maxn]; int r1,r2; int siz[maxn],dfn[maxn],dfscnt; void dfs(int u) { siz[u]=1;dfn[u]=dfscnt; for(auto v:g[u]) dfs(v),siz[u]+=siz[v]; } struct bit { int c[maxn]; void clr() { for(int i=1;i<=n;i) c[i]=0; } int lowbit(int x){return x&(-x);} void ad(int x,int k){while(x<=n){c[x]+=k;x+=lowbit(x);}} int qry(int x){int res=0;while(x>=1){res+=c[x];x-=lowbit(x);}return res;} }T1,T2; int F[maxn],st[maxn],tp,sz[maxn],son[maxn]; vector e[maxn]; void dfs1(int u) { st[tp]=u;e[u].clear(); if(tp>k) F[u]=st[tp-k]; else F[u]=0; if(F[u]) e[F[u]].push_back(u); sz[u]=1;son[u]=0; for(auto v:h[u]) { dfs1(v); if(sz[v]>sz[son[u]]) son[u]=v; sz[u]+=sz[v]; } tp–; } void ins(int x,int k) { T1.ad(dfn[x],k);T1.ad(dfn[x]+siz[x],-k); T2.ad(dfn[x],k); } int qry(int x) { return T1.qry(dfn[x])+T2.qry(dfn[x]+siz[x]-1)-T2.qry(dfn[x]-1); } void dfs3(int u,int ty) { for(auto v:e[u]) { if(ty1) ans+=qry(v); else if(ty3) ins(v,-1); else if(ty==2) ins(v,1); } for(auto v:h[u]) dfs3(v,ty); } void dfs2(int u,int ty) { for(auto v:h[u]) if(v!=son[u]) dfs2(v,0); if(son[u]) { dfs2(son[u],1); for(auto v:h[u]) if(v!=son[u]) dfs3(v,1),dfs3(v,2); } for(auto v:e[u]) ins(v,1); if(!ty) dfs3(u,3); } void cal() { dfscnt=0;dfs(r1); T1.clr();T2.clr(); tp=0;dfs1(r2); dfs2(r2,0); } void solve() { cin>>n>>k; for(int i=1;i<=n;i) cin>>p[i]; for(int i=1;i<=n;i++) cin>>q[i]; for(int i=1;i<=n;i++) { if(!p[i]) r1=i; else g[p[i]].push_back(i); if(!q[i]) r2=i; else h[q[i]].push_back(i); } cal(); for(int i=1;i<=n;i++) swap(p[i],q[i]),swap(g[i],h[i]);swap(r1,r2); cal(); cout<<ans<<“\n”; } signed main() { freopen(“D.in”,“r”,stdin); freopen(“D.out”,“w”,stdout); ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); =1; //cin>>; while(–) { solve(); } return 0; } //by cristiano ronaldo dos santos aveiro #include <bits/stdc++.h> using namespace std; typedef long long ll; #define rep(i,s,t) for(register ll i = s;i <= t;++i) #define per(i,t,s) for(register ll i = t;i >= s;–i) const ll N = 1e6 + 5; ll n; ll k; ll rt1; ll rt2; ll top; ll idx; ll ans; ll p[N] = {}; ll q[N] = {}; ll fa[N] = {}; ll st[N] = {}; ll sz[N] = {}; ll siz[N] = {}; ll dfn[N] = {}; ll son[N] = {}; vector g[N]; vector g1[N]; vector g2[N]; class binary_indexed_tree { private: ll t[N] = {}; public: inline void init() { memset(t,0,sizeof(t)); } inline ll lowbit(ll x) { return x & (-x); } inline void upd(ll x,ll k) { while(x <= n) { t[x] += k; x += lowbit(x); } } inline ll qry(ll x) { ll ans = 0; while(x) { ans += t[x]; x -= lowbit(x); } return ans; } }; binary_indexed_tree t1; binary_indexed_tree t2; inline ll read() { ll x = 0; ll y = 1; char c = getchar(); while(c < ‘0’ || c > ‘9’) { if(c == ‘-’) y = -y; c = getchar(); } while(c >= ‘0’ && c <= ‘9’) { x = (x << 3) + (x << 1) + (c ^ ‘0’); c = getchar(); } return x * y; } inline void write(ll x) { if(x < 0) { putchar(‘-’); write(-x); return; } if(x > 9) write(x / 10); putchar(x % 10 + ‘0’); } inline void dfs(ll u) { siz[u] = 1; dfn[u] = ++idx; for(register auto v : g1[u]) { dfs(v); siz[u] += siz[v]; } } inline void dfs1(ll u) { st[++top] = u; g[u].clear(); if(top > k) fa[u] = st[top - k]; else fa[u] = 0; if(fa[u]) g[fa[u]].push_back(u); sz[u] = 1; son[u] = 0; for(auto v : g2[u]) { dfs1(v); if(sz[v] > sz[son[u]]) son[u] = v; sz[u] += sz[v]; } top–; } inline void ins(ll x,ll k) { t1.upd(dfn[x],k); t1.upd(dfn[x] + siz[x],-k); t2.upd(dfn[x],k); } inline ll query(ll x) { return t1.qry(dfn[x]) + t2.qry(dfn[x] + siz[x] - 1) - t2.qry(dfn[x] - 1); } inline void dfs3(ll u,ll k) { for(auto v : g[u]) { if(k == 1) ans += query(v); else if(k == 2) ins(v,1); else if(k == 3) ins(v,-1); } for(auto v : g2[u]) dfs3(v,k); } inline void dfs2(ll u,ll k) { for(auto v : g2[u]) if(v != son[u]) dfs2(v,0); if(son[u]) { dfs2(son[u],1); for(auto v : g2[u]) { if(v != son[u]) { dfs3(v,1); dfs3(v,2); } } } for(register auto v : g[u]) ins(v,1); if(!k) dfs3(u,3); } int main() { freopen(“D.in”,“r”,stdin); freopen(“D.out”,“w”,stdout); n = read(); k = read(); rep(i,1,n) p[i] = read(); rep(i,1,n) q[i] = read(); rep(i,1,n) { if(!p[i]) rt1 = i; else g1[p[i]].push_back(i); if(!q[i]) rt2 = i; else g2[q[i]].push_back(i); } idx = 0; dfs(rt1); t1.init(); t2.init(); top = 0; dfs1(rt2); dfs2(rt2,0); rep(i,1,n) { swap(p[i],q[i]); swap(g1[i],g2[i]); swap(rt1,rt2); } idx = 0; dfs(rt1); t1.init(); t2.init(); top = 0; dfs1(rt2); dfs2(rt2,0); write(ans); fclose(stdin); fclose(stdout); return 0; }针对以下问题,上述两段代码的功能有什么不同,请指出并修正第二段代码,使得第二段代码功能与第一段代码功能完全等价小丁的树 题目描述 小丁拥有两棵均具有 n n 个顶点,编号集合为 { 1 , 2 , ⋯ &thinsp; , n } {1,2,⋯,n} 的有根树 T 1 , T 2 T 1 ​ ,T 2 ​ ,现在他需要计算这两棵树的相似程度。 为了计算,小丁定义了对于一棵树 T T 和 T T 上两个不同顶点 u , v u,v 的距离函数 d T ( u , v ) d T ​ (u,v),其定义为 u , v u,v 两个点距离成为祖先关系有多近,具体来说,对于所有在 T T 上为祖先关系的点对 ( u ′ , v ′ ) (u ′ ,v ′ ), dis ⁡ ( u , u ′ ) + dis ⁡ ( v , v ′ ) dis(u,u ′ )+dis(v,v ′ ) 的最小值即为 d T ( u , v ) d T ​ (u,v) 的值,其中 dis ⁡ ( u , v ) dis(u,v) 表示 u , v u,v 在树 T T 上的唯一简单路径包含的边数,即 u , v u,v 的距离。 点对 ( u ′ , v ′ ) (u ′ ,v ′ ) 为祖先关系,当且仅当 u ′ u ′ 是 v ′ v ′ 的祖先或 v ′ v ′ 是 u ′ u ′ 的祖先。(注意,每个点都是自己的祖先) 小丁心里还有一个参数 k k,如果节点对 ( u , v ) (u,v) 满足以下条件,称之为不相似的节点对: 1 ≤ u < v ≤ n 1≤u<v≤n " d T 1 ( u , v ) 0 d T 1 ​ ​ (u,v)=0 且 d T 2 ( u , v ) k d T 2 ​ ​ (u,v)>k“ 或 " d T 2 ( u , v ) 0 d T 2 ​ ​ (u,v)=0 且 d T 1 ( u , v ) k d T 1 ​ ​ (u,v)>k​“ 小丁认为,不相似的节点对越多, T 1 T 1 ​ 和 T 2 T 2 ​ 就越不相似,你能告诉他总共有多少不相似的节点对吗? 输入格式 第一行两个整数 n , k n,k,表示 T 1 T 1 ​ 和 T 2 T 2 ​ 的节点数和参数 k k。 第二行 n n 个正整数 p 1 , p 2 , ⋯ &thinsp; , p n p 1 ​ ,p 2 ​ ,⋯,p n ​ , T 1 T 1 ​ 中节点 i i 的父节点为 p i p i ​ ,特别的,若 p i 0 p i ​ =0,则 i i 是 T 1 T 1 ​ 的根。 第三行 n n 个正整数 q 1 , q 2 , ⋯ &thinsp; , q n q 1 ​ ,q 2 ​ ,⋯,q n ​ , T 2 T 2 ​ 中节点 i i 的父节点为 q i q i ​ ,特别的,若 q i 0 q i ​ =0,则 i i 是 T 2 T 2 ​ 的根。 输出格式 一行一个整数,表示不相似的节点对总数。 样例 1 输入 5 0 0 1 1 2 3 5 3 1 1 0 样例 1 输出 4 样例 1 解释 ( 2 , 3 ) , ( 2 , 4 ) , ( 2 , 5 ) , ( 4 , 5 ) (2,3),(2,4),(2,5),(4,5) 为不相似的节点对。 其余样例见下发文件。 数据规模与约定 对于所有数据, 1 ≤ n ≤ 2 × 10 5 , 0 ≤ k < n , 0 ≤ p i , q i ≤ n 1≤n≤2×10 5 ,0≤k<n,0≤p i ​ ,q i ​ ≤n,且由 p i , q i p i ​ ,q i ​ 形成的是一棵 n n 个节点的有根树。 本题采用捆绑评测,你只有通过了一个子任务中所有测试点才能得到该子任务的分数。 Subtask 1(10pts): 1 ≤ n ≤ 100 1≤n≤100。 Subtask 2(20pts): 1 ≤ n ≤ 3000 1≤n≤3000。 Subtask 3(20pts): k 0 k=0。 Subtask 4(10pts): 0 ≤ k ≤ 20 0≤k≤20。 Subtask 5(40pts):无特殊限制。
最新发布
07-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值