LCA 离线算法 tarjan

from:http://blog.youkuaiyun.com/zxy_snow/article/details/6151884

kosaraju算法是从算导上看证明看明白的。然后我觉得,GB以前说的话很有道理,他挺想让我们看算导,看上面的证明,做课后题,神马的。现在看来,确实有用。

kosaraju比较好理解,准备学tarjan,可恨的是,算导只提了这个算法一句话 = =。百度谷歌后,好的学习文章基本没有,我想要详细证明的那种。但是现在看来,基本都是解释算法,不带证明的,YM。我想起来原版论文了。搜出来了。

DEPTH-FIRST SEARCH AND LINEAR GRAPH ALGORITHMS(http://dutta.csc.ncsu.edu/csc791_spring07/wrap/dfs.pdf)

纯英版的。好吧。Tarjan大神写的。。。1972年出版的。。。膜拜下。

昨晚看的时候,觉得没有想象中那么难,一页基本只有两三个生词,压力不大。

看证明的时候有点纠结了,虽然我都知道那词的意思,但是句子表达的含义有点理解不了,YM。

硬看这都是。。。 = =。。。只用看其中5页就好了,前面是DFS的证明神马的。

昨晚睡觉前,看图,自己心里模拟了下,居然懂了 。。。激动~~~

今早起来,看着伪代码,把tarjan给写了。把poj的2186用tarjan给过了,虽然需要再理解下。

--------------

POJ题目:1330


### 使用 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` 参数作为输入参数,并返回对应每一对顶点之间最小公祖节点的结果数组。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值