【图论】无向图连通性(tarjan算法)

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

割边:dfn[u]<low[v]

割点:dfn[u]<=low[v] (若为根节点,要有两个v这样的点)

一.知识点:

1.连通

在图论中,连通性是指一个无向图中的任意两个顶点之间存在路径。如果对于图中的任意两个顶点 u 和 v,存在一条路径从 u 到 v,那么图被称为连通图。如果图不是连通的,那么它可以被分为多个连通分量,每个连通分量都是一个连通子图。

2.割点:

割点(Cut Vertex),也称为关节点或割顶,是指在无向图中,如果移除该顶点及其相连的边,会导致图不再连通,那么该顶点就被称为割点。

3.割边:

割边(Cut Edge),也称为,是指在无向图中,如果移除该边,会导致图不再连通,那么该边就被称为割边。

割边在图中起到了连接不同连通分量的作用,其移除会导致图的连通性发生变化。

 4.tarjan算法:(选择性阅读)

【图论】无向图连通性(tarjan算法),图论,图论,算法

 Tarjan算法是一种用于寻找有向图中强连通分量(Strongly Connected Components,简称SCC)的算法,由Robert Tarjan在1972年提出。强连通分量是指在有向图中,任意两个顶点之间存在双向路径。

Tarjan算法使用深度优先搜索(DFS)来遍历图,并通过维护一个栈和一些辅助数据结构来识别强连通分量。算法的基本思想是通过DFS遍历图中的每个顶点,并为每个顶点记录其访问次序(Discovery Time)和能够回溯到的最早的祖先顶点(Lowest Ancestor)。通过这些信息,可以识别出具有相同祖先的顶点集合,即一个强连通分量。

Tarjan算法的步骤如下:

  1. 对图中的每个顶点进行深度优先搜索(DFS)遍历。
  2. 在DFS遍历的过程中,为每个顶点记录其访问次序和最早祖先顶点。
  3. 将已访问的顶点入栈。
  4. 当DFS遍历回溯到一个顶点时,检查该顶点的最早祖先顶点。如果最早祖先顶点是自身,则将栈中的顶点弹出,并将这些顶点构成一个强连通分量。
  5. 重复步骤3和步骤4,直到遍历完所有的顶点。

Tarjan算法的时间复杂度为O(V+E),其中V是顶点数,E是边数。它是一种高效的算法,常被用于解决与强连通分量相关的问题,如图的缩点、强连通分量的数量和结构等。

总之,Tarjan算法是一种用于寻找有向图中强连通分量的算法,通过DFS遍历和栈的运用,可以高效地找到图中的所有强连通分量。


二.讲解 

在此之前,先介绍两个数组;

int dfn[];里面存放访问顺序(时间戳);

int low[];里面存放追溯值(即祖先节点最小的dfn)

(1)割边

tarjan提出:(证明可以自行百度)

当dfn[u]<low[v]时,连接这两条点的边为割边(重边要特殊处理,后面介绍)

(2)割点

tarjan提出:(证明可以自行百度)

当dfn[u]<=low[v]时,u这个点为割点(若为根节点,要有两个v这样符合条件的点)


三.割边

(1)题目

P1656 炸铁路 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述:

找出割边

输入:

第一行输入两个整数n和m,表示点和边的个数。

第i(2<=i<=2+m)行,每行输出两个数字,表示一条边的两个点。

输出:

割边

样例输入:

6 7
1 2
1 3
2 4 
2 5
3 4
4 5
4 6

样例输出:

4---6

(2)初代码 

/*
6 7
1 2
1 3
2 4 
2 5
3 4
4 5
4 6
*/

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
int n,m;
struct Edge{
	int u,v,next;
}edge[maxn<<1];
int cnt,head[maxn];
void add(int u,int v){
	edge[++cnt]=(Edge){u,v,head[u]}; head[u]=cnt;
}
int num,dfn[maxn],low[maxn];
void tarjan(int u,int fa){
	dfn[u]=low[u]=++num;
	for(int i=head[u];i;i=edge[i].next){
		int v=edge[i].v;
		if(v==fa) continue;
		if(dfn[v]==0){
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<low[v]){ //割边条件 ,若>则表示v不止和u相连 
				cout<<u<<"----"<<v<<endl; 
			}
		}else{
				low[u]=min(low[u],dfn[v]);
			}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	int u,v;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&u,&v);
		add(u,v); add(v,u);
	}
	tarjan(1,0);
	return 0;
}

(3)bug与解答

1.若这张图有多个连通分量怎么办?

答:遍历即可

	for(int i=1;i<=n;i++){
		if(dfn[i]==0)  tarjan(1,0);
	}

2.若有重边怎么办?结果显然不对。

答:只continue,第二次让这段代码运行

【图论】无向图连通性(tarjan算法),图论,图论,算法

然后就无法满足 dfn[u]<low[v]条件了

		if(v==fa){
			k++; //防止重边 
			if(k==1) continue;
		} 

(4)最终代码

#include<bits/stdc++.h>
#define maxn 5005
using namespace std;
int n,m;
struct Edge{
	int u,v,next;
}edge[maxn<<1];
int head[maxn],cnt;
void add(int u,int v){
	edge[++cnt]=(Edge){u,v,head[u]}; head[u]=cnt;
}
int dfn[maxn],low[maxn],num;
struct node{
	int u,v;
}ans[maxn];
int an;
bool cmp(node a,node b){
	if(a.u!=b.u) return a.u<b.u;
	else return a.v<b.v;
}
void tarjan(int u,int fa){
	int k=0;
	dfn[u]=low[u]=++num;
	for(int i=head[u];i;i=edge[i].next){
		int v=edge[i].v;
		if(v==fa){
			k++;
			if(k==1) continue;
		}
		if(dfn[v]==0){
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<low[v]){
				if(v<u) swap(u,v);
				ans[++an]=(node){u,v}; 
			}
		}else{
			low[u]=min(low[u],dfn[v]);
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);add(v,u);
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]) tarjan(i,0);
	}
	sort(ans+1,ans+an+1,cmp);
	for(int i=1;i<=an;i++){
		cout<<ans[i].u<<" "<<ans[i].v<<endl;
	}
	return 0;
}

四.割点

其实只是微改动一下即可。其次就是可以优化一下。函数传参只需要传u,无需判断是否为父节点。因为不会影响结果。(自行参考代码推理)

再次强调:若为根节点,要有两个v这样的点!

参考代码:文章来源地址https://www.toymoban.com/news/detail-635071.html

/*
6 7
1 2
1 3
2 4 
2 5
3 4
4 5
4 6
*/
 
#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
int n,m;
struct Edge{
	int u,v,next;
}edge[maxn<<1];
int cnt,head[maxn];
void add(int u,int v){
	edge[++cnt]=(Edge){u,v,head[u]}; head[u]=cnt;
}
int ans[maxn],an;
int num,dfn[maxn],low[maxn],root;
void tarjan(int u){
	dfn[u]=low[u]=++num;
	int flag=0;
	for(int i=head[u];i;i=edge[i].next){
		int v=edge[i].v;
		if(dfn[v]==0){
			tarjan(v);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<=low[v]){ //割点条件 
				flag++;
				if(u!=root || flag>1) ans[u]=1;
			}
		}else{
				low[u]=min(low[u],dfn[v]);
			}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	int u,v;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&u,&v);
		add(u,v); add(v,u);
	}
	//防止本来就有不连通的 
	for(int i=1;i<=n;i++){
		if(dfn[i]==0){
			root=i;
			tarjan(i);
		} 
	}
	for(int i=1;i<=n;i++){
		if(ans[i]) an++;
	}
	cout<<an<<endl;
	for(int i=1;i<=n;i++){
		if(ans[i])
			cout<<i<<" ";
	}
	return 0;
}

到了这里,关于【图论】无向图连通性(tarjan算法)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

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

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

    2024年02月05日
    浏览(37)
  • 【学习笔记】无向图的连通性

    定义: 在无向图连通图中,若把点 (x) 删去后整个图就不连通了,则 (x) 为割点(割顶)。 朴素方法: 每次删去一个点,然后判断图是否连通,时间复杂度为 (O(n(n+m))) 。 Tarjan 算法: (dfn_x) : (x) 被 dfs 到的时间戳 (low_x) :在 (x) 及以后被搜索的所有节点的 (low) 值

    2024年02月15日
    浏览(31)
  • 数据结构|连通图、完全图、无向图、有向图的边数计算问题

    完全图 也称简单完全图。一个图任意两个顶点之间都有边的话,该图就称为完全图。 连通图(一般都是指无向图) 如果图中任意俩顶点都连通,则该图为连通图。 有向图 由点和弧所构成的图( 强连通图必然是有向图,因为强连通和弱连通的概念只在有向图中存在 ) 无向

    2023年04月08日
    浏览(36)
  • 图论——邻接矩阵之无向网

    在此之前,我们需要先理清图和网的区别 1.图G:有两个集合,边集V和点集E【点集用来存放各个顶点,边集用来存放各条边来表示关联两点的联系】 2.权值:即即两顶点之间互相往来需要花费的代价或消耗 3.网:带权值的图 所谓邻接矩阵,即用矩阵排布的方式来构建两点之间

    2024年02月05日
    浏览(29)
  • 图论12-无向带权图及实现

    在无向无权图的基础上,增加边的权。 使用TreeMap存储边的权重。 遍历输入文件,创建TreeMap adj存储每个节点。 每个输入的adj节点链接新的TreeMap,存储相邻的边和权重 两条边相连,则分别把权重加入各自的邻接表中 判断两点之间是否有边 求相邻的所有节点 求两点的权值 移

    2024年04月26日
    浏览(24)
  • 第三章 图论 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日
    浏览(29)
  • 图论01-【无权无向】-图的基本表示-邻接矩阵/邻接表

    https://github.com/Chufeng-Jiang/Graph-Theory/tree/main/src/Chapt01_Adjacency 代码有删减 代码有删减 只需要改动一行 adj = new TreeSet[V]; //构造邻接表, V行,V个LinkedList 代码有删减

    2024年02月07日
    浏览(31)
  • B3610 [图论与代数结构 801] 无向图的块 题解

    2023 2023 2023 ,再见。 2024 2024 2024 ,你好! 其实就是统计点双连通分量的个数。需要注意的是,孤立点在这里不被看作块。本文使用 tarjan 算法来解决这道题。 概念明晰 时间戳:这里记为 d f n i dfn_i df n i ​ ,表示第一次深度优先搜索到节点 i i i 的时间。时间 t i m e ∈ N + t

    2024年02月03日
    浏览(33)
  • 第三章 图论 No.10无向图的双连通分量

    无向图有两种双连通分量 边双连通分量,e-DCC 点双连通分量,v-DCC 桥:删除这条无向边后,图变得不连通,这条边被称为桥 边双连通分量:极大的不含有桥的连通区域,说明无论删除e-DCC中的哪条边,e-DCC依旧连通 (该连通分量的任意边属于原图中的某条环)。此外,任意两

    2024年02月12日
    浏览(28)
  • 图论--强连通分量

    描述 给出一个有向图,求该图的强连通分量的个数。 输入描述 多测试用例,每个测试用例: 第一行给出顶点数  n  ( 1 ≤  n  ≤ 1000 ) 第二行给出边数  e  ( 0 ≤  e  ≤ 100000 ) 第三行开始,共  e  行,每行两个正整数  a b ,表示从顶点  a  发出一条弧到顶点  b  。 输出

    2024年02月13日
    浏览(25)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包