poj1330 lca_tarjan || 并查集

本文介绍了一种基于深度优先搜索框架的离线算法Tarjan,该算法利用并查集实现LCA问题的O(n+Q)解法,其中Q为询问次数。文章详细解释了Tarjan算法的工作原理及其实现过程。

此题是lca裸问题,而且居然问题还每次还只有1个,貌似水的过去,但是为了看rmq和lca的转化,就先把tarjan给搞定了吧。。。

离线算法 Tarjan

利用 并查集优越的时空复杂度,我们可以实现LCA问题的O(n+Q)算法,这里Q表示询问的次数。Tarjan算法基于 深度优先搜索的框架,对于新搜索到 的一个结点,首先创建由这个结点构成的集合,再对当前结点的每一个子树进行搜索,每搜索完一棵子树,则可确定子树内的LCA询问都已解决。其他的LCA询 问的结果必然在这个子树之外,这时把子树所形成的集合与当前结点的集合合并,并将当前结点设为这个集合的祖先。之后继续搜索下一棵子树,直到当前结点的所 有子树搜索完。这时把当前结点也设为已被检查过的,同时可以处理有关当前结点的LCA询问,如果有一个从当前结点到结点v的询问,且v已被检查过,则由于 进行的是 深度优先搜索,当前结点与v的最近公共祖先一定还没有被检查,而这个最近公共祖先的包涵v的子树一定已经搜索过了,那么这个最近公共祖先一定是v 所在集合的祖先。

 

具体分析可以参见:http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html

 

此题貌似并查集也可以做。因为只有一个询问,只要探寻a的父亲和b的父亲,当两者相等的时候就可以了。时间慢一点,也是可以过的。

但是不推荐啊。学新知识才是啊。

### 使用 Tarjan 算法求解最近公共祖先(LCA) #### 定义与特性 Tarjan 算法属于离线算法的一种,在一次遍历中可以解决所有询问,因此具有 \(O(n+q)\) 的时间复杂度[^1]。该算法适用于树结构中的多个节点对之间的 LCA 查询。 #### 实现原理 Tarjian 算法的核心在于利用深度优先搜索(DFS)和并查集的数据结构来高效地找到两个节点的最低共同祖先。具体来说: - 对于每一个节点 u 和它的子节点 v 进行 DFS 访问; - 当访问到某个节点时,将其加入当前路径集合 S 中,并标记为已访问状态; - 如果遇到一个已经被访问过的节点,则说明找到了回路,此时可以通过查找并查集中代表元的方式确定这些节点间的 LCA 关系; - 处理完所有的边之后再撤销本次操作的影响以便后续其他分支继续执行相同逻辑; #### Python 代码实现 下面是一个基于上述思路编写的Python版本的Tarjan算法实现: ```python from collections import defaultdict, deque class UnionFind(object): def __init__(self, n): self.parent = list(range(n)) def find(self, p): if self.parent[p] != p: self.parent[p] = self.find(self.parent[p]) return self.parent[p] def union(self, p, q): rootP = self.find(p) rootQ = self.find(q) if rootP == rootQ: return self.parent[rootP] = rootQ def tarjan_lca(edges, queries): graph = defaultdict(list) for u, v in edges: graph[u].append(v) graph[v].append(u) uf = UnionFind(len(graph)) result = {} visited = set() query_dict = {tuple(sorted([u,v])) : idx for idx,(u,v) in enumerate(queries)} def dfs(node,parent=None): nonlocal result,visited,query_dict stack = [(node,False)] while stack: node,is_backtracked=stack.pop() if not is_backtracted: visited.add(node) # Process all adjacent nodes except parent. children=[child for child in graph[node]if child!=parent] stack.append((node,True)) for next_node in reversed(children): if next_node not in visited: stack.extend([(next_node,False)]) continue # Backtracking phase starts here. for other_node,lca_index in ((other_node,idx)for (a,b),idx in query_dict.items()if a==node or b==node): if other_node in visited: lca=min(other_node,node,key=lambda x:uf.find(x)) result[lca_index]=lca uf.union(parent,node) start_node = min(graph.keys()) dfs(start_node) output = [-1]*len(query_dict) for i,j in result.items(): output[i]=j return output ``` 此函数接收图表示形式 `edges` 及查询列表 `queries` 参数作为输入参数,并返回对应每一对顶点之间最小公祖节点的结果数组。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值