tarjan算法(相关概念、Tarjan求最大强连通分量、割点、求桥、缩点)

news2024/11/17 19:39:25

什么是连通分量

无向图 G 的最大连通子图称为 G 的连通分量

注:这里的最大连通子图的含义为:此图为 G 的连通子图,将 G 的任意一个点加到盖子图中之后,此子图将不再连通

比如这一张图中,很显然,我用三种颜色圈起来的部分都是原图的连通分量。(其实可以十分形象的理解成每一个连通分量都是一个连通块)回想到以前的“亲戚”一题,就是让我们去求在这个无向图中,询问的两个点是不是在同一个连通分量中。值得注意的一点,一个连通图的连通分量就是他本身。

关于有向图图的连通的一些概念

假如在一个有向图中对于每一对点 vi,vj 都存在 vi->vj 的路径和  vj->vi 的路径(值得注意的是,这里是存在一条路径,而不是存在一条边),那么就称之为强连通图

很显然,这一张图就是一个强连通图。有向图 G 的子图是强连通图,那么称该图为强连通子图

显然,在上图中 (1, 2, 3) 和 (4, 5, 6) 都是强连通子图

有向图 G 的最大强连通子图则称为 G 的强连通分量(这里的“最大”和之前是一个意思),显然,在上图中,最大强连通子图为图本身。

关于无向图的连通的一些概念

双连通图:在任意两个点之间都存在至少 2 条不相交可以理解为不重叠,我原本理解了半天)路径的图。

割点:如果删掉点 v 和与 v 相关联的边,那么得到新图至少有两个双连通分量, 那么称点 v 为割点。

:如果无向图中的桥后(桥是一条边),得到的新图包含两个连通分量

双连通图不包含割点的无向连通图。

双连通分量:无向连通图的最大双连通子图

点双连通分量:通过找割点获得的双连通分量

边双连通分量:通过找获得的双连通分量

为了举例子,在这里我继续使用上面的那一张图(换为无向)。

在图中,只有 (3, 4) 一条边,而割点只有 3、4 两个点。

Tarjan 算法

原本还有一个 kosaraju 算法,因为没有什么用,在这里就不专门介绍了。

这里我们用 dfn[i] 表示编号为 i 的节点在 dfs 的过程中的遍历顺序,就是一个 dfs 序。(也可以叫时间戳)

low[i] 表示 i 节点及其下方节点所能到达的开始时间最早的节点的开始时间。(初始时 low[i] = dfn[i]

这里有 1 个性质:因为在 dfs 的过程中会形成一棵搜索树,所以在越上面的节点显然 dfn 就会越小

如果发现一个点有边连到了搜索树中的自己的祖宗节点,那么就更新其 low 的值

关于 low 值与 dfn 值

1、如果一个节点的 low 值小于 dfn 值,那么就说明它或者它的子孙节点有边连到自己上方的节点

2、如果一个节点的 low 值等于 dfn 值,则说明其下方的节点不能走到其上方节点,那么该节点就是一个强连通分量在搜索树中的根

3、但是 u 的子孙节点就未必和 u 处于同一个强连通分量,用栈存储即可(具体看代码)。

代码实现:

void tarjan(int u)
{
	dfn[u] = low[u] = ++ dn; // 将 dfn 和 low 都赋值为编号 dn
	sta[ ++ tt] = u, vis[u] = 1, st1[u] = 1;
	// 将当前点 u 加入栈中,标记为访问过,已经入栈
	for (int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		if (!vis[j]) // 如果这个点还没有被访问过,就访问
		{
			tarjan(j); // 继续搜这个点的儿子节点
			low[u] = min(low[u], low[j]);
			// 更新这个点的 low 值为 low[u] 和 low[j] 的较小值
		}
		else if (st1[j]) // 如果这个点已经在栈中了
			low[u] = min(low[u], dfn[j]);
			// 那么就把这个点和这个子节点的 dfn 取最小值
	}
	int t;
	if (dfn[u] == low[u])
	// dfn[u] == low[u] 表示 u 是一个强连通分量的跟根
	{
		scc ++ ;
		// 统计连通分量的个数
		do
		{
			t = sta[tt -- ];
			st1[t] = 0;
			// 取出,标记为不在栈中
			cout << t << ' ';
		}while (u != t);
		cout << '\n';
		//	输出最大强连通分量
	}
}

当然,要求整张图最大连通分量,就要在主函数中这样调用它:

for (int i = 1; i <= n; i ++ )
		if (!dfn[i]) tarjan(i);

这样就可以求出所有的最大连通分量了!

用 Tarjan 算法求割点和桥

割点:如果一个点为 割点,那么当且仅当满足性质 (1) (2)。

(1):u 为树根,且有超过一个子树

(2):u 不为树根,且满足存在 (u, v) 为树枝边,使得 dfn(u) <= low(v)

:如果一条无向边 (u, v) 是桥,当且仅当 (u, v) 为树枝边,且满足 dfn[u] < low[v]前提是这条边不存在重边,不然删掉之后还是有边)。

关于割点,可以参考这个代码:

#include <bits/stdc++.h>
#define pb push_back

using namespace std;
const int N = 200010, M = N * 2, mod = 998244353;
typedef pair<int, int> PII;
typedef long long ll;
typedef unsigned long long ull;

int n, m, root;
int dn, dfn[N], low[N], cnt, tt, sta[N];
int h[N], e[M], ne[M], w[M], idx, scc;
bool st[N], st1[N], vis[N];
void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void tarjan(int u)
{
	int son = 0;
	dfn[u] = low[u] = ++ dn;
	sta[ ++ tt] = u, vis[u] = 1, st1[u] = 1;
	for (int i = h[u]; ~i; i = ne[i])
	{
		int j = e[i];
		if (!vis[j])
		{
			tarjan(j), son ++ ;
			low[u] = min(low[u], low[j]);
			/*
			if (dfn[u] < low[v])
				cout << u << ' ' << v << '\n';
			输出桥 
			*/
			if (low[j] >= dfn[u] && u != root)
				cnt += !st[u], st[u] = 1;
		}
		else if (st1[j])
			low[u] = min(low[u], dfn[j]);
	}
	if (son >= 2 && u == root)
		cnt += !st[u], st[u] = 1;
	int t;
	/*
	if (dfn[u] == low[u])
	{
		scc ++ ;
		do
		{
			t = sta[tt -- ];
			st1[t] = 0;
			cout << t << ' ' 
		}while (u != t)
		cout << '\n';
	}
	输出最大强连通分量 
	*/
}

int main()
{
	memset (h, -1, sizeof h);
    ios::sync_with_stdio(false);
	cin >> n >> m;
	for (int i = 1; i <= m; i ++ )
	{
		int u, v;
		cin >> u >> v;
		add (u, v), add(v, u);
	}
	for (int i = 1; i <= n; i ++ )
		if (!dfn[i]) root = i, tarjan(i);
	cout << cnt << '\n';
	for (int i = 1; i <= n; i ++ )
		if (st[i]) cout << i << ' ';
    return 0;
}

这个是洛谷 P3388 的代码,桥的代码和最大连通分量的代码也在其中。

关于 Tarjan 缩点的方法

这里草草的说一下,其实缩点就在 Tarjan 求强连通分量的时候用一个 id 数组和 cnt 数组,存一下每一个点在哪一个强连通分量中和强连通分量的大小,就可以了,实现如下:

if (dfn[u] == low[u])
{
	scc ++ ;
	do
	{
		t = sta[tt -- ];
		st1[t] = 0;
        id[t] = scc, cnt[scc] ++ ;
//		cout << t << ' ';
	}while (u != t)
//	cout << '\n';
}

关于 Tarjan 算法的一些介绍就说到这里了,下次再见 ~~。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/734286.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何识别网站是否有WAF

使用工具识别&#xff1a;wafw00f https://github.com/EnableSecurity/wafw00f 前提&#xff1a;本地配置好python环境安装&#xff1a;在此目录下创建CMD窗口&#xff0c;输入 python setup.py install 。安装成功后&#xff0c;显示版本信息. 使用&#xff1a;进入wafw00…

QSystemTrayIcon简单使用

效果&#xff1a; 关键程序&#xff1a; trayIcon new QSystemTrayIcon(this);trayIcon->setIcon(QIcon(":/images/开心果.png"));trayIcon->show();trayIcon->showMessage(ui->title->text(),ui->msg->toPlainText(),QIcon(":/images/睡眠…

路径规划算法:基于堆优化优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于堆优化优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于堆优化优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法…

雅思考试报名条件及时间是什么时候?

雅思考试&#xff08;IELTS&#xff09;是国际英语语言测试系统&#xff0c;是著名的国际性英语标准化水平测试之一。想要出国留学或者就业都是需要有语言成绩的&#xff0c;其中雅思考试也是大家参加的比较多的语言考试之一&#xff0c;那么雅思考试报名条件及时间是什么时候呢…

为初学者准备的 Dubbo 入门教程

Dubbo Dubbo 与 RPC 的关系 Dubbo 是一种开源的分布式服务框架&#xff0c;由阿里巴巴公司开发。它为应用程序提供高性能的 RPC&#xff08;远程过程调用&#xff09;通信和服务治理能力&#xff0c;让应用程序能够在分布式环境中快速构建高可靠性和可扩展性的服务。Dubbo 核心…

信道的容量和复用

信道的极限容量 当信道质量比较差时&#xff0c;输出信号的波形难以识别&#xff0c;此时出现的现象称为码间串扰“ 失真的因素有&#xff1a;码元传输速率&#xff0c;信号传输距离&#xff0c;噪声干扰&#xff0c;传输媒体质量等。 奈奎斯特准则&#xff1a; 理想低…

18.Lucas-Kanade光流及OpenCV中的calcOpticalFlowPyrLK

文章目录 光流法介绍OpenCV中calcOpticalFlowPyrLK函数补充reference 欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f339; 光流法介绍 光流描述了像素在图像中的运动&#xff0c;就像彗星☄划过天空中流动图像。同一个像素&#xff0c;随着时…

机器学习与深度学习——利用随机梯度下降算法SGD对波士顿房价数据进行线性回归

机器学习与深度学习——利用随机梯度下降算法SGD对波士顿房价数据进行线性回归 我们这次使用随机梯度下降&#xff08;SGD&#xff09;算法对波士顿房价数据进行线性回归的训练&#xff0c;给出每次迭代的权重、损失和梯度&#xff0c;并且绘制损失loss随着epoch变化的曲线图。…

集群 第四章

目录 1. nginx、lvs、haproxy 的区别 2. 实验 3. ssh 升级 4.总结 1. nginx、lvs、haproxy 的区别 2. 实验 Haproxy 服务器&#xff1a;192.168.83.101 Nginx 服务器1&#xff1a;192.168.83.102 Nginx 服务器2&#xff1a;192.168.83.103 …

Mysql之视图,索引及数据的备份与恢复

目录 一、视图 1.视图是什么 2.视图与数据表的区别 3.视图的优缺点 优点&#xff1a; 缺点&#xff1a; 4.视图的应用场景 5.语法运用 二、索引 1.什么是索引 2.为什么要使用索引 3.使用索引的优缺点 4.何时不使用索引 5.索引何时失效 6.索引分类 三、数据的备份…

头结点到底方便了啥?

头结点到底方便了啥&#xff1f; 链表增加头结点的作用如下&#xff1a; (1)便于首元结点的处理 (2)便于空表和非空表的统一处理 (参考&#xff1a;《数据结构 C语言(第2版)》P31) 其实这两句话很抽象&#xff0c;你说方便就方便&#xff0c;你倒是举个粟子或者画个图什么的啊&…

Linux开发工具之vim工具的使用介绍

目录 前言 1.vim的基本概念 命令模式(Normal mode) 插入模式(Insert mode) 末行模式(last line mode) 2.vim的基本操作 命令模式的命令集 移动光标 ​编辑 删除文字 复制 替换 撤销操作 更改 vim末行模式命令集 简单vim配置 总结 前言 大家好呀&#xff0c;许久…

Java动态规划LeetCode1137. 第 N 个泰波那契数

方法1&#xff1a;通过动态规划解题&#xff0c;这道题也是动态规划的一道很好的入门题&#xff0c;因为比较简单和容易理解。 代码如下&#xff1a; public int tribonacci(int n) {//处理特殊情况if(n0){return 0;}if(n1||n2){return 1;}//定义数组int[]dpnew int[n1];//初…

浏览器通过js打开文件,新建文件,静默实时保存文件

资源&#xff0c;点击下载 在线访问Txt Markdown &#x1f61d;&#x1f61d;&#x1f61d;&#x1f61d;&#x1f61d;&#x1f61d; 新建文件后&#xff0c;可以直接保存文件&#xff0c;不需要再次下载文件&#xff0c;也只有第一次保存时候才会出现确认弹窗 html <!D…

尚硅谷React学习笔记(上)

目录 一、React入门 1.1、React简介 为什么要学&#xff1f; React的特点 1.2、React的基本使用 Hello React案例 创建虚拟DOM的两种方式 虚拟DOM与真实DOM 1.3、React JSX 语法规则 JSX小练习 1.4、模块与组件化的理解 模块 组件 模块化 组件化 二、React面向…

E. Scuza - 二分+前缀和

分析&#xff1a; 暴力会超时&#xff0c;可以用二分&#xff0c;构建两个数组&#xff0c;一个是a[i]&#xff0c;作为前缀和数组&#xff0c;一个是f[i]表示第i个台阶之前的最大高度的台阶&#xff0c;然后每次二分来查找k&#xff0c;因为尽可能地走的多&#xff0c;所以查找…

VTK STL 体积 表面积测量 最短路径 读取中文路径

目录 开发环境&#xff1a; vtkMassProperties 三、中文路径 数据读取 开发环境&#xff1a; 系统&#xff1a;Win10 VTK&#xff1a;8.2.0 Qt&#xff1a;5.12.4 一、结构化对象 体积 面积 vtkMassProperties VTK 计算体积和面积的主要类 vtkMassProperties vtkSm…

C语言进阶之指针的进阶

指针的进阶 1. 字符指针2. 指针数组3. 数组指针3.1 数组指针的定义3.2 &数组名VS数组名3.3 数组指针的使用 4. 数组参数、指针参数4.1 一维数组传参4.2 二维数组传参4.3 一级指针传参4.4 二级指针传参 5. 函数指针6. 函数指针数组7. 指向函数指针数组的指针8. 回调函数9. 指…

【程序员必须掌握的算法】【Matlab】GRNN神经网络遗传算法(GRNN-GA)函数极值寻优——非线性函数求极值

上一篇博客介绍了BP神经网络遗传算法(BP-GA)函数极值寻优——非线性函数求极值&#xff0c;神经网络用的是BP神经网络&#xff0c;本篇博客将BP神经网络替换成GRNN神经网络&#xff0c;希望能帮助大家快速入门GRNN网络。 1.背景条件 要求&#xff1a;对于未知模型&#xff08;…

使用trtexec工具多batch推理tensorrt模型(trt模型)

文章目录 零、pt转onnx模型一、onnx转trt模型二、推理trt模型 零、pt转onnx模型 参考&#xff1a;https://github.com/ultralytics/yolov5 用根目录下的export.py可以转pt为onnx模型&#xff0c;命令如下可以转换成动态batch的onnx模型 python3 export.py --weights./yolov5s…