本问题中Tarjan算法不需要设置栈和dfn,low标号,而是设置了并查集。
通过一次dfs遍历即可找出所有节点对的lca。将这样一次读取所有查询,计算一次后返回所有查询lca的算法称为离线算法
涉及到在线算法和离线算法这两个概念的算法还有区域最值查询问题(RMQ问题)。
以下方法均可用于有向图【先查找出根节点root,再DFS】和无向图【root可以任意设置或者是直接遍历所有节点】
原图和询问可以存储为【链式向前型】和【邻接表】的形式,注意定义形式即可。
【1】使用union函数
void LCA_Tarjan(int u)
{
int now_to;
int i;
vis[u]=1; //标记+访问+遍历
ancestor[u]=u;
for(i=Tnode[u].pre;i!=-1;i=Tedge[i].pre)
{
now_to=Tedge[i].to_vertex;
if(!vis[now_to])
{
LCA_Tarjan(now_to);
Union(u,now_to);
ancestor[Find(now_to)]=u; //ancestor[Find(u)]=u; 效果一样
}
}
for(i=Qnode[u].pre;i!=-1;i=Qedge[i].pre)
{
now_to=Qedge[i].to_vertex;
if(vis[now_to])
{
lca[Qedge[i].num]=ancestor[Find(now_to)]; //单独开数组存储
// Qedge[2*Qedge[i].num-1].ans=Qedge[2*Qedge[i].num].ans=ancestor[Find(now_to)];
}
}
}
【2】不使用union函数
void LCA_Tarjan(int u)
{
int now_to;
int i;
vis[u]=1; //标记+访问+遍历
f[u]=u; //将其父亲(根)指向自己
for(i=Tnode[u].pre;i!=-1;i=Tedge[i].pre) /
{
now_to=Tedge[i].to_vertex;
if(!vis[now_to])
{
LCA_Tarjan(now_to);
f[now_to]=u; //存储当前最近公共祖先
}
}
for(i=Qnode[u].pre;i!=-1;i=Qedge[i].pre)
{
now_to=Qedge[i].to_vertex;
if(vis[now_to]) //如果其子节点及子节点的子树全部访问完才会进入这一步,由vis判断
{ //如果已经访问了问题节点,就可以返回结果
lca[Qedge[i].num]=Find(now_to);
// Qedge[2*Qedge[i].num-1].ans=Qedge[2*Qedge[i].num].ans=Find(now_to); /*均可*/
}
}
}