关于最近公共祖先之倍增思想

思路:

此做法用了 st 表、二进制拆分等方法。这些方法是我所不常用的,所以才会出现被两个0卡了半小时的情况。

倍增思想分为3部分:①建表。 ②两点化成同一高度。 ③两点一起找祖宗。

d e p i dep_i depi 存结点深度, f a i j fa_{ij} faij 存结点向上走 2 j 2^j 2j

①:此处用深搜,从树根开始,对每个节点进行操作,把每个节点的深度确定好,在遍历19次(树的深度不超过 2 19 2^{19} 219),每次遍历时确定 f a [ u ] [ i ] = f a [ f a [ u − 1 ] [ i − 1 ] ] [ i − 1 ] fa [ u ][ i ] =fa [ fa [ u - 1 ][ i - 1 ] ][ i - 1 ] fa[u][i]=fa[fa[u1][i1]][i1]

②:用 u,v 记录当前两点的位置,且 u 为深度更深的点,接下来从 2 19 2^{19} 219 开始,如果 u 往上跳 2 i 2^i 2i 格仍处于 v 下方,那么就跳上去(保持 u 始终在 v 下方)。遍历结束后,这时的 u 和 v 就变成高度一样的点了。如果 u 和 v 重合了,那么 v 就是最近公共祖先。此时即可直接输出结果了。

③:如果都第二步都结束了还执行第三步,说明到了同一高度却没有重合,这时就需要两个点一起往上跳了。只要两点没重合,就一直往上跳,当循环完了后,此时的两点的上面一个点就是它们的公共祖先了。

核心代码:

void dfs(int u,int f){//  步骤一
	dep[u]=dep[f]+1;
	fa[u][0]=f;
	for(int i=1;i<=19;i++){
		fa[u][i]=fa[fa[u][i-1]][i-1];
	}
	for(int i:e[u]) if(i!=f)dfs(i,u);
}
int lca(int u,int v){//  步骤二
	if(dep[u]<dep[v])swap(u,v);
	for(int i=19;i>=0;i--){
		if(dep[fa[u][i]]>=dep[v])u=fa[u][i];	
	}
	if(v==u)return u;
	for(int i=19;i>=0;i--){//  步骤三
		if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
	}
	return fa[u][0];
}

另外两种也很不错的方法:

### 关于最近公共祖先 (LCA) 算法 #### 定义与概念 最近公共祖先(Least Common Ancestor, LCA)是指在一个有根树中,找到任意两个节点 \( u \) 和 \( v \) 的最深的共同祖先。这里的“最深”指的是距离根节点路径最长的那个祖先节点【^3】。 #### 主要算法方法 针对 LCA 问题,常见的解决方法包括暴力法、倍增法以及 Tarjan 法: 1. **暴力法** 暴力解法通过逐层遍历的方式寻找两节点的公共祖先。这种方法的时间复杂度较高,在大规模数据下效率较低【^1】。 2. **倍增法** 倍增法是一种高效的在线算法,其核心思想是利用二进制跳跃表来快速定位目标节点的位置。具体来说,先将两个节点调整至同一深度,再逐步向上跳转直至两者相遇。此过程可以通过预处理父节点数组实现加速操作【^4】。 3. **Tarjan 离线算法** Tarjan 方法属于一种离线批量查询方案,适合处理大量固定查询请求的情况。它基于并查集的思想构建连通分量关系图,并在此基础上完成所有询问的回答【^2】。 以下是采用 Python 编写的简单版倍增法求解 LCA 的代码示例: ```python import math def preprocess(n, adj_list, root): log = int(math.log2(n)) + 1 parent = [[-1]*log for _ in range(n)] depth = [-1] * n stack = [(root, -1, 0)] while stack: node, par, d = stack.pop() if depth[node] != -1: continue depth[node] = d parent[node][0] = par for i in range(1, log): if parent[node][i-1] != -1: parent[node][i] = parent[parent[node][i-1]][i-1] for child in adj_list[node]: if child != par: stack.append((child, node, d+1)) return parent, depth def lca(u, v, parent, depth): if depth[u] < depth[v]: u,v=v,u diff=depth[u]-depth[v] log=len(parent[0]) for i in reversed(range(log)): if(diff&(1<<i)): u=parent[u][i] if u==v:return u for i in reversed(range(log)): if parent[u][i]!=-1 and parent[u][i]!=parent[v][i]: u=parent[u][i] v=parent[v][i] return parent[u][0] # Example Usage adjacency_list = {0:[1,2], 1:[3,4], 2:[5,6], 3:[], 4:[], 5:[], 6:[]} N = 7 PARENTS, DEPTHS = preprocess(N, adjacency_list, 0) print(lca(3, 6, PARENTS, DEPTHS)) # Output should be '0' ``` 上述程序展示了如何使用倍增技术预先计算每个节点的不同层次上的祖先信息以便后续高效检索任何一对节点之间的最小公祖宗位置。 #### Java 实现案例 另外还存在其他编程语言版本比如下面这个来自 C++ 博客园作者分享过的完整 java 版本解决方案链接可以直接访问获取更多信息【^5】
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值