Tarjan算法 (以发现者Robert Tarjan命名)是一个在图中寻找强连通分量的算法。算法的基本思想为:任选一结点开始进行深度优先搜索dfs(若深度优先搜索结束后仍有未访问的结点,则再从中任选一点再次进行)。搜索过程中已访问的结点不再访问。搜索树的若干子树构成了图的强连通分量。
应用到咱们要解决的LCA问题上,则是:对于新搜索到的一个结点u,先创建由u构成的集合,再对u的每颗子树进行搜索,每搜索完一棵子树,这时候子树中所有的结点的最近公共祖先就是u了。
以上转自: v_JULY_v http://blog.csdn.net/v_july_v/article/details/18312089
求最近公共祖先的离线算法主要运用的就是 dfs+并查集。
使用情况为预先知道所要查询的所有点的信息。
时间复杂度能达到O(n+m)。(n为结点个数,m为查询次数)
Tarjan离线算法是基于深度优先搜索的,在用其求解LCA问题是,又充分利用了并查集的优越性,具体方法为:
从根节点开始先下搜索
每搜到一个节点就判断当前节点及其所有的子节点是否都已访问过:
若没有访问完,则继续深搜当前节点的所有子节点,当一个节点及其所有的子节点都访问完后将其所有子节点的父节点设置为该节点,然后标记该节点为已访问;
若访问完,则判断所查询的点里面是否有当前节点:
若有,则判断在查询的点里面与当前节点对应的点是否访问过:
若访问过,则他们的最近公共祖先就是与当前节点对应的点在并查集中的父节点;
若没有访问过,则继续深搜查询。
若没有,则继续深搜查询。
关键代码如下:
const int INF=10005;
int vis[INF],fa[INF];
int top;
struct Edge
{
int node;
int id;
int next;
}edge[INF]; //edge存储的是每条边的信息
struct qEdge
{
int node;
int id;
int next;
}qedge[INF*2]; //qedge存储所查询的点的信息(记得要建双向的边)
void AddEdge(int u,int v)
{
edge[top].node=v;
edge[top].next=edge[u].id;
edge[u].id=top++;
}
void qAddEdge(int u,int v)
{
qedge[top].node=v;
qedge[top].next=qedge[u].id;
qedge[u].id=top++;
}
void Init()
{
memset(fa,-1,sizeof(fa));
memset(vis,0,sizeof(vis));
memset(edge,-1,sizeof(edge));
memset(qedge,-1,sizeof(qedge));
top=0;
}
int Find(int x) //并查集
{
return fa[x] == -1 ? x : Find(fa[x]);
}
void Union(int u,int v) //更新并查集
{
int x=Find(u);
int y=Find(v);
if(x!=y)
fa[y]=x;
}
void Tarjan(int node)
{
vis[node] = 1 ;
for(int i=edge[node].id; i!=-1 ; i=edge[i].next)
{
int no=edge[i].node;
if(!vis[no])
{
Tarjan(no);
Union(node,no);
}
}
for(int i=qedge[node].id; i!=-1 ; i=qedge[i].next)
{
int no=qedge[i].node;
if(vis[no])
{
printf("%d\n",Find(no));
}
}
}