tarjan算法

tarjan算法的各类应用

前言:
    学习tarjan也有这么长时间了,想尝试着写篇博客来记下自己对tarjan的理解。

PS:因为在下的能力有限,对tarjan的理解还不够深刻,所以会继续钻研tarjan,这篇博客会作为我的一个笔记本,不断的更新。

求强连通分量

既然我们要求强连通分量,那么我们得先弄清,什么是强连通分量?

在有向图中,如果两个顶点u,v间有一条路(u,v),也有一条路(v,u),那么则称这两个顶点是强连通的

如果有向图的任意两个顶点都强连通,则称该图是一个强连通图

有向非强连通图中的强连通子图,称为强连通分量

求割点    

什么是割点?

在一个无向连通图中,如果有一个点集合,删去这个点集合后,原图变得不连通,就称这个点集合为割点集合。

这里我们再引进来一个概念:
一个图的点连通度:最小割点集合中的元素个数。

如果一个无向连通图的点连通度大于一,则称这个图是点双联通的,简称双连通重连通

当且仅当一个无向连通图的点连通度为一,那么该图的最小割点集合中,唯一的那个元素,唯一的那条点,叫做割点

割点又该如何判断呢?

当一个顶点x是割点时,它只需满足:

  1. u为树根,且u的子树个数大于1。 
  2. u不为树根,且满足(u,v)为树枝边,并使得dfn[u]<=low[v]。

以上两点只需满足一个就行了。

第一点我们很好理解,当这个点为树根,且有多个子树时,割去这个树根点,剩下的图自然不会联通。在程序中我们可以表现为:

if(u==father)++cnt;

if(u==father&&cnt>1)cut[u]=1;

第二点也还行,其实就是u在搜索树中为v的父亲,或者说u和v之间有一条边,且u的dfs序要小于等于v的祖先的dfs序,就等于在搜索的时候,是先搜索了u,再搜索了v的祖先(甚至u就是v的祖先),所以删去u之后,v以及v的子树都无法连接到u的祖先,如此一来,u自然也是个割点。在程序中我们可以表示为:

if(u!=father&&dfn[u]<=low[v])cut[u]=1;

到这里,割点判断的核心内容已经差不多了,但是由割点衍生出来的许多题型都需要我们从这最基础的上面再加一些骚操作。

在下实力也有限,无法具体指出来,只能靠各位多做题多总结,找到方法,提升熟练度了。

割点tarjan的模板如下:

void tarjan(int u,int father)
{
	dfn[u]=low[u]=++num;
	st[++top]=u;
	int cnt=0;
	for(int i=first[u];i;i=nex[i])
	{
		int v=to[i];
		if(!dfn[v])
		{
			tarjan(v,father);
			low[u]=min(low[u],low[v]);
			if(u!=father&&dfn[u]<=low[v])
				cut[u]=1;
			if(u==father)++cnt;
		}
		low[u]=min(low[u],dfn[v]);
	}
	if(cnt>=2&&u==father)
		cut[u]=1;
}

求割边与桥

什么是割边?什么是桥?

在一个无向连通图中,如果有一个边集合,删去这个边集合后,原图变得不连通,就称这个边集合为割边集合。

这里我们再引进来一个概念:
一个图的边连通度:最小割边集合中的元素个数。

如果一个无向连通图的边连通度大于一,则称这个图是边双联通的,简称双连通重连通

当且仅当一个无向连通图的边连通度为一,那么该图的最小割边集合中,唯一的那个元素,唯一的那条边,叫做

在很多的题目中,我们都会遇到需要求一张图的桥的情况,不妨开动脑经想想,怎么样求一个桥?

判断桥的条件如下:

若一条无向边(u,v)是桥,则当且仅当(u,v)是树枝边,且满足dfn[u]<low[v]。

这句话怎么理解?

dfn[u]是u的dfs序,low[v]是v的祖先的dfs序,当dfn[u]<low[v]时,则代表先搜索u,再搜索v的祖先,v想要到达u的祖先就必须经过(u,v)这条边,删去(u,v),该图便不会再联通。

现实情况中,可能会有重边的情况出现,所以我们就用一个father变量代表这条边的起始,再加以判断,防止它在重边时再次进行low[]值的更换。

代码实现如下:

void tarjan(int u,int father)
{
    dfn[u]=low[u]=++num;
    for(int i=first[u];i;i=next[i])
    {
        int v=to[i];
        if(!dfn[v])
        {
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(dfn[u]<low[v])
            {
                bridge[++cnt][0]=u;
                bridge[cnt][1]=v;
            }
        }
        else if(v!=father)
            low[u]=min(low[u],dfn[v]);
    }
}


 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
LCA(最近公共祖先)是指在一棵树中,找到两个节点的最近的共同祖先节点。而Tarjan算法是一种用于求解强连通分量的算法,通常应用于有向图中。它基于深度优先搜索(DFS)的思想,通过遍历图中的节点来构建强连通分量。Tarjan算法也可以用于求解LCA问题,在有向无环图(DAG)中。 具体来说,在使用Tarjan算法求解LCA时,我们需要进行两次DFS遍历。首先,我们从根节点开始,遍历每个节点,并记录每个节点的深度(即从根节点到该节点的路径长度)。然后,我们再进行一次DFS遍历,但这次我们在遍历的过程中,同时进行LCA的查找。对于每个查询,我们将两个待查询节点放入一个查询列表中,并在遍历过程中记录每个节点的祖先节点。 在遍历的过程中,我们会遇到以下几种情况: 1. 如果当前节点已被访问过,说明已经找到了该节点的祖先节点,我们可以更新该节点及其所有后代节点的祖先节点。 2. 如果当前节点未被访问过,我们将其标记为已访问,并将其加入到查询列表中。 3. 如果当前节点有子节点,我们继续递归遍历子节点。 最终,对于每个查询,我们可以通过查询列表中的两个节点的最近公共祖先节点来求解LCA。 需要注意的是,Tarjan算法的时间复杂度为O(V+E),其中V为节点数,E为边数。因此,对于大规模的树结构,Tarjan算法是一种高效的求解LCA问题的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值