Tarjan 算法——图论学习笔记

这篇具有很好参考价值的文章主要介绍了Tarjan 算法——图论学习笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Tarjan 算法——图论学习笔记

Part.1 引入

在图论问题中,我们经常去研究一些连通性问题,比如:

  • 有向图的联通性:传递闭包——Floyd 算法;
  • 有向图连通性的对称性:强联通分量(SCC)——Tarjan 算法缩点;
  • 无向图的联通性:并查集;
  • 无向图的关键边:桥(割边)、边双——Tarjan 算法缩点;
  • 无向图的关键点:割点、点双——Tarjan 建立圆方树。

那么,Tarjan 算法到底是什么呢?

Part.2 Tarjan 算法求 SCC

SCC,即强联通分量,是一张有向图的极大子图,满足任意两个点 u , v u,v u,v 强联通(即 u u u 可以到 v v v v v v 可以到 u u u)。一个重要的性质就是强联通具有传递性

在有向图中,我们会用 Tarjan 算法去建一棵 DFS 生成树:

Tarjan 算法——图论学习笔记,学习笔记,算法,图论,图搜索算法

在 DFS 的过程中,我们会对于每个点,处理出一个 DFS 序号,并把 DFS 到的点放入一个栈中,定义一个新数组 l o w low low,其中, l o w x low_x lowx 表示 x x x 号节点能跳到的在栈中的 DFS 序的最小值。

接下来就是 Tarjan 算法的核心思想:对于一个点 u u u,如果满足 d f n u = l o w u dfn_u=low_u dfnu=lowu,说明点 u u u 不存在向上跳的非树边,是一个顶点。那么当前节点及以后在栈中的节点就构成了一个 SCC。

代码如下:

int dfn[N],low[N],idx,stk[N],top,scc[N],c;
bool inc[N];
void dfs(int u)
{
	dfn[u] = low[u] = ++idx,stk[++top] = u,inc[u] = 1;
	for(int i = head[u];i;i = nxt[i])//建 DFS 树且维护 low 数组
	{
		int v = to[i];
		if(!dfn[v])//树边
		{
			dfs(v);
			low[u] = min(low[u],low[v]);
		}
		else if(inc[v]) low[u] = min(low[u],dfn[v]);//非树边
	}
	if(dfn[u]==low[u])//是一个顶点,构成一个 SCC
	{
		c++;//SCC个数+1
		while(stk[top]!=u)
			inc[stk[top]] = 0,scc[stk[top]] = c,top--;
		inc[u] = 0,scc[u] = c,top--;
	}
}

求 SCC 有什么用呢?

  • 在一些与连通性有关的问题中,我们把每个 SCC 缩成一个点,可以得到一个 DAG,因为如果还环的话就能形成一个新的 SCC。这样就可以在 DAG 上做 DP 之类来解决问题;
  • 在 2-SAT 问题(有 m m m 个二元方程,并且每个未知数都是 0 0 0 1 1 1)中,我们求解和判无解也需要用到 SCC,有兴趣的可以自己去看。

Part.3 Tarjan 求割边、边双

无向图中,定义割边(也叫桥)为删掉这条边后图的连通性改变的边。边双连通分量(简称边双),即为不存在割边的极大子图,与 SCC 一样,具有传递性。

和求 SCC 一样,我们还是去建立一颗 DFS 树。我们重新定义 l o w x low_x lowx x x x 号节点最多通过一条非树边能跳到的 DFS 序的最小值。那一条连接 u , v u,v u,v 的边是桥当且仅当 d f n u < l o w v dfn_u<low_v dfnu<lowv,就相当于 v v v 经过一条非树边到不了 u u u,这样肯定是割边。

至于求边双,就是每次选一个点,在不经过桥的前提下能到的所有点就形成了一个边双。

一个细节就是边表的 cnt 记得赋值为 1 1 1,方便求反向边。

代码和求 SCC 差不多,具体如下:

int n,m,dfn[N],low[N],t;
bool brg[M];
void dfs(int u,int ine)//求割边
{
	dfn[u] = low[u] = ++t;
	for(int i = head[u];i;i = nxt[i])
	{
		if(i==ine) continue;//不往回走
		int v = to[i];
		if(dfn[v]==0)
		{
			dfs(v,i^1);
			low[u] = min(low[u],low[v]);
			if(low[v]>dfn[u]) brg[i] = brg[i^1] = 1;//是割边
		}
		else low[u] = min(low[u],dfn[v]);
	}
}
bool vis[N];
int c;//记录边双的个数
void dfs(int u)
{
	ans[c].push_back(u),vis[u] = 1;
	for(int i = head[u];i;i = nxt[i])
	{
		if(brg[i]) continue;//不经过桥
		int v = to[i];
		if(!vis[v]) dfs(v);
	}
}

Part.4 Tarjan 求割点、点双

参照割边、边双的定义,割点为删掉这个点后图的连通性改变的点。点双连通分量(简称点双),即为不存在割点的极大子图。但是点双不具有传递性,所以点双是最难的。画一个图就知道了:

Tarjan 算法——图论学习笔记,学习笔记,算法,图论,图搜索算法

左边 4 4 4 个点是个点双,右边 4 4 4 个点也是点双,但是整张图却不是点双,因为中间的点是个割点。

仍然建出 DFS 树,发现用顶点找出一个点双已经不可行了,因为一个点能在多个点双当中。

Tarjan 算法——图论学习笔记,学习笔记,算法,图论,图搜索算法

我们发现,两个点双至多有一个交点,这个点就是割点。如上图,这个交点一定在下面的点双中 d f n dfn dfn 是最小的,而上面的点双可以由另外的点(类似树根)求出。

所以,对于一条边 ( u , v ) (u,v) (u,v),如果满足 l o w v ≥ d f n u low_v\ge dfn_u lowvdfnu,就说明找到一个点双了。

上代码:

int n,m,dfn[N],low[N],c,stk[N],top,t;
vector<int> ans[N];
void dfs(int u,int fa)
{
	dfn[u] = low[u] = ++t;
	stk[++top] = u;
	int son = 0;
	for(int i = head[u];i;i = nxt[i])
	{
		int v = to[i];
		if(v==fa) continue;
		if(dfn[v]==0)
		{
			++son;
			dfs(v,u);
			low[u] = min(low[u],low[v]);
			if(low[v]>=dfn[u])//找到点双
			{
				++c;
				while(stk[top]!=v) ans[c].push_back(stk[top--]);
				ans[c].push_back(v);--top;ans[c].push_back(u);//细节:不能将点 u 弹出栈
			}
		}
		else low[u] = min(low[u],dfn[v]);
	}
	if(fa==0&&son==0) ans[++c].push_back(u);//特判:单独一个点也是点双
}

点双有一个巨大的应用——圆方树。

圆方树,就是对于每个点双,新建一个方点,断掉点双原图中的边,然后点双里所有的点向这个方点连边。这样形成的结构就是一棵树。举个例子:

Tarjan 算法——图论学习笔记,学习笔记,算法,图论,图搜索算法

这样,图上问题就变成了树上问题,是不是简单很多?

那圆方树还有什么应用呢?

  • 求 可行路径/必经点 的一些东西。可行路径就对应圆方树上两点的路径加上与树上路径的方点直接相连的点,必经点就是树上路径的圆点;

  • 仙人掌图:比如 P5236,通过重新定义边权,将仙人掌上最短路变为树上路径。

顺便提一句,图上问题就变成了树上问题大多都要判断两点 LCA 为方点的情况。

Part.5 总结

Tarjan 算法在图论问题中有大用,特别是有关联通性的题。

写字不易,给个赞吧~
THE END \text {THE END} THE END文章来源地址https://www.toymoban.com/news/detail-785636.html

到了这里,关于Tarjan 算法——图论学习笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【算法每日一练]-图论(保姆级教程篇12 tarjan篇)#POJ3352道路建设 #POJ2553图的底部 #POJ1236校园网络 #缩点

    目录: 今天知识点 加边使得无向图图变成双连通图 找出度为0的强连通分量 加边使得有向图变成强连通图 将有向图转成DAG图进行dp          POJ3352:道路建设         思路: POJ2553:图的底部 思路: POJ1236校园网络 思路: 缩点:  思路:          由于道路要维修

    2024年02月05日
    浏览(43)
  • 算法基础复盘笔记Day07【搜索与图论】—— Prim、Kruskal、染色体判定二分图、匈牙利算法

    ❤ 作者主页:欢迎来到我的技术博客😎 ❀ 个人介绍:大家好,本人热衷于 Java后端开发 ,欢迎来交流学习哦!( ̄▽ ̄)~* 🍊 如果文章对您有帮助,记得 关注 、 点赞 、 收藏 、 评论 ⭐️⭐️⭐️ 📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉 1. 题目

    2024年02月02日
    浏览(50)
  • 算法基础复盘笔记Day06【搜索与图论】—— Dijkstra、bellman-ford、spfa、Floyd

    ❤ 作者主页:欢迎来到我的技术博客😎 ❀ 个人介绍:大家好,本人热衷于 Java后端开发 ,欢迎来交流学习哦!( ̄▽ ̄)~* 🍊 如果文章对您有帮助,记得 关注 、 点赞 、 收藏 、 评论 ⭐️⭐️⭐️ 📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉 1. 题目

    2023年04月22日
    浏览(46)
  • 「学习笔记」tarjan求最近公共祖先

    Tarjan 算法是一种 离线算法 ,需要使用并查集记录某个结点的祖先结点。 并没有传说中的那么快。 将询问都记录下来,将它们建成正向边和反向边。 在 dfs 的过程中,给走过的节点打上标记,同时维护并查集,这里利用了回溯的思想,如果 (u) 节点的这棵子树没搜完,那么

    2024年02月01日
    浏览(41)
  • 「学习笔记」tarjan 求最近公共祖先

    Tarjan 算法是一种 离线算法 ,需要使用并查集记录某个结点的祖先结点。 并没有传说中的那么快。 将询问都记录下来,将它们建成正向边和反向边。 在 dfs 的过程中,给走过的节点打上标记,同时维护并查集,这里利用了回溯的思想,如果 (u) 节点的这棵子树没搜完,那么

    2024年02月01日
    浏览(50)
  • 进阶搜索算法 学习笔记

    DATE 20231031:补充一道好题 P4872 OIer们的东方梦(Dijkstra 分层图最短路)。 双向广搜、双向深搜 堆优化的 Dijkstra 一颗小小的 A-STAR 不大聪明的 IDDFS(IDS) 可爱的 IDA-STAR 这是进阶搜索算法,不说了直接上例题: 以“P1514 引水问题”为例: 点击查看代码 算法思想 在搜索的时候

    2024年02月09日
    浏览(44)
  • Hello算法学习笔记之搜索

    一、二分查找 1.从数组中找到target的索引  注意:while条件是=  O(logn) 二分查找并非适用于所有情况,原因如下: 二分查找仅适用于有序数据。若输入数据无序,为了使用二分查找而专门进行排序,得不偿失。因为排序算法的时间复杂度通常为 (O(n log n)) ,比线性查找

    2024年02月11日
    浏览(49)
  • 【算法】复习搜索与图论

    🍎 博客主页:🌙@披星戴月的贾维斯 🍎 欢迎关注:👍点赞🍃收藏🔥留言 🍇系列专栏:🌙 蓝桥杯 🌙请不要相信胜利就像山坡上的蒲公英一样唾手可得,但是请相信,世界上总有一些美好值得我们全力以赴,哪怕粉身碎骨!🌙 🍉一起加油,去追寻、去成为更好的自己!

    2024年02月05日
    浏览(46)
  • 第三章 图论 No.8最近公共祖先lca, tarjan与次小生成树

    O ( m l o g n ) O(mlogn) O ( m l o g n ) ,n为节点数量,m为询问次数,lca是一种在线处理询问的算法 自己也是自己的祖先 倍增: f a ( i , j ) fa(i, j) f a ( i , j ) 表示从i开始,向上走 2 j 2^j 2 j 步走到的点 j = 0,走到父节点 j 0,分两步走,先走到 2 j − 1 2^{j-1} 2 j − 1 步再走 2 j − 1 2^{

    2024年02月13日
    浏览(44)
  • 算法基础课-搜索与图论

    题目链接:842. 排列数字 - AcWing题库 思路:写的很好的题解AcWing 842. 排列数字--深度优先遍历代码+注释 - AcWing 也可以考虑使用c++自带的next_permutation函数直接秒了: 题目链接:844. 走迷宫 - AcWing题库 思路:由于bfs是一层一层扩展,所以能保证走到终点时,走过的距离最短,所

    2024年04月15日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包