C++图论之强连通图
1. 连通性
什么是连通性?
连通,字面而言,类似于自来水管道中的水流,如果水能从某一个地点畅通流到另一个地点,说明两点之间是连通的。也说明水管具有连通性,图中即如此。
无向图和有向图的连通概念稍有差异。
无向图连通性
如果任意两点间存在路径,称此图具有连通性,如下的图结构具有连通性。提及连通性,就不得不说连通分量,通俗而言,指结构中有多少个连通通道,如下的图结构只有一个连通通道,也就是一个连通分量,所有节点在这个连通通道上都能互通。
而下图,则有2
个连通分量。1,2,3,4,5
可以在一个连通通道上互通,不能和6,7
互通。6,7
在自己的连通通道上可以互通。
如何检查图结构的连通性和计算连通分量?
笨拙的方案自是使用深度或广度搜索算法。原理较简单,一次搜索完毕后,搜索到的节点必是在一个连通分量上。如果一次搜索完毕后被搜索出来的节点数量和图结构原有的节点数量相同,可证明只有一个连通分量。否则,可以再次从除第一次搜索出来的节点之外的节点开始重新搜索,再检查搜索出来的节点数量……如此如此,便可以检测出所有连通分量。
在性能要求不高的应用场景,这是不错的选择。否则,可以使用轻巧、快速的并查集数据结构来检查。
有向图的连通性
无论是在有向图或无向图中,都不可能改变连通这个概念。区别于有向图中的边有方向,无向图中的连通可以认为是双向通道,可认为是广义连通;有向图中的连通则是单向通道,可认为是狭义连通。
有向图中,如果一个节点能通过单向通道到达另一个节点,可认为这两点之间是连通的。如下图中,4->1、2->4->1
是连通的,而2-3
是不连通的。讨论连通的局部性没有太大意义,有向图中讨论的是强连通性。
什么强连通?
强连通是有向图的特定概念。有向图中,任意两点之间都可以连通,则认定此有向图为强连通图,如下图。
连通分量用来记录连通通道的数量,有向图中的连通分量指强连通分量。如上图,有一个强连通分量,也称此图为强连通性有向图。
如下图所示有向图结构,有向图本身不具有强连通性,但存在子图具有强连通性,则称子图即为原图的强连通分量。
当然,具有强连通性的子图可能不只一个。猜一猜,下图有几个连通分量。
我们已知在无向图中计算连通分量的算法。那么在有向图中如何计算机强连通分量?
算法界有一句名言:没有暴力算法不能解决的问题。有向图中查找强连通子量,同样可以使用深度搜索或广度搜索。可以说,在树和图论问题中没有广度和深度搜索算法解决不了的。说起来感觉很历害,道理却是简单,任何问题都是在能搜索到的前提下得到解决的。
直接使用广度或深度搜索,毫无疑问属于暴力算法。虽然这是一条康庄大道,但是,不一定是一条捷径之路。好吧,现在让我们去发现是否有捷径小道。
2. Tarjan
算法
Tarjan
算法很优秀,也很优雅,颇有风淡云轻,四两拨千金之感。理解Tarjan
算法,先要知晓几个概念,如DFS
序、时间戳、回溯值……这些可以查阅我的文章《DFS序和欧拉序的降维打击》。
Tarjan
可以解决很多问题。如公共祖先、割点、割边……当然还有本文的强连通分量的求解。
理解Tarjan
算法求解强连通分量的工作机制之前,先搞明白有向图的 DFS
生成树中的 4 种边。
- 树边
(tree edge)
:节点与节点之间的边。 - 反祖边
(back edge)
:上图中以红色边表示(即7->1
),也被叫做回边,即指向祖先节点的边。 - 横叉边
(cross edge)
:上图中以蓝色边表示(即9->7
),搜索的时候遇到的一个已经访问过的结点,但是这个结点 并不是 当前结点的祖先。 - 前向边
(forward edge)
:上图中以绿色边表示(即3->6
),在搜索的时候遇到子树中的结点的时候形成的。搜索过程所有前向边组成一条搜索分支。
DFS
生成树与强连通分量之间的关系:
如果结点 u
是某个强连通分量在搜索树中遇到的第一个结点,那么这个强连通分量的其余结点肯定是在搜索树中以 u
为根的子树中。结点 u
被称为这个强连通分量的根。
以下图的结构为例,讲解查找强连通分量的流程。
准备变量
- 栈
sta
,存储强连通分量上的所有节点; dfn
记录节点的时间戳,一个结点的子树内结点的dfn
都大于该结点的dfn
。也可以记录节点是否被访问过。low(回溯值)
记录节点通过一条不在搜索树上的边能到达的结点。或者说不经过直接父节点能访问到的最早(远)的祖先,或者说是经过回边访问到的祖先节点。
搜索过程
- 从节点
1
开始深度搜索,记录每一个节点在DFS
时的时间戳以及回溯值。如1
号节点的刚开始的时间戳为1
,回溯值为1
。别忘记了,1
号节点现在也在栈中。
- 按深度搜索路线,一路下去,后面应该是
2、5、4
。下图给出了当搜索到4
号节点时,每一个节点的时间戳和回溯值以及栈中的状态。此时栈中由栈底到栈顶存储着一条DFS
搜索树:1->2->5->4
。
-
当从
4
号节点访问到2
号节点时,转机出现了。因为,2
节点被访问过,现又以4
号节点的子节点身份重新被访问,会想到是不是碰到了祖先,或者说遇到了同一个强连通分量上的节点?答案是,不能这么简单的认为?因为这种情况有可能是回边也有可能是横叉边。
如下图所示。
从
1
号开始深度搜索,在第一条深度搜索分支结束后,4
号节点也会被标记为被访问过。回溯到1
号节点后,会开始第二条分支,在再次搜索到4
号节点时,同样会发现4
号节点也被访问。难道说4
号节点和1
号节点在同一个强连通分量上吗?4->2
是回边,而1->4
是横叉边。
那么应该如何做出正确判断?继续回到我们的图结构上来讨论怎么正确得到强连通分量。
下图中2
号节点在栈中,说明早于4
号节点被访问到且还没有加入其它的强连通分量上,可以判断2
是4
号的祖先。所以节点是否在栈中,是判断是不是回边的一个很重要的条件。
- 于是,更新
4
号节点的low[4]=2
。既然4
号节点能到达2
号节点,显然,点4
的父节点们也能通过4
号节点到达2
号节点……一脉相承吗?于是这些节点的low
值得以更新。
4
号节点除了2
号节点外没有其它子节点,搜索结束,回溯到4
号,因其dfs[4]!=low[4]
,暂时不要出栈,继续回溯到5
号节点,因为dfs[5]!=low[5]
,不出栈。继续回溯到2
号节点,因其dfs[2]==low[2]
,说明一个强连通分量到2
号节点结束。把它们从栈中弹出来,得到第一个强连通分量上的所有节点。
**Tips:**如果
i
节点的dfn[i]!=low[i]
,说明其节点可以回到更早的祖先。也说明,其在以祖先开始的强连通分量上。所以只有一直回溯到祖先时,才能一一出栈。Tarjan
算法求解强连通分量中,栈起到了至关重要的作用。一旦发现一个强连通分量,就会把这个强连通分量上的节点弹出来。所以访问过、但是不在栈中的节点说明已经加入到了另一个强连通分量上。如果访问过,但是还在栈中的节点说明还没有找到归属。
- 回溯到
1
号节点时,因dfn[1]=low[1]
。1
号节点构成只有一个节点的强连通分量。
小结:
- **深度搜索阶段:**如果
u
节点的子节点v
已经访问、且在栈中。说明v
是u
的祖先,更新u
的low
值。同时更新u
的除了v
之外祖先的low
值。 - **回溯阶段:**如果
u
节点的dfn!=low
,继续向上回溯, 如果u
节点的dfn==low
,说明找到了一个强连通分量,把栈中u
节点(包含u
)之上的节点全部弹出来。
编码实现
#include<iostream>
#include<stack>
#include<vector>
using namespace std;
//节点数、边数
int n,m;
//dfn记数器和强连通分量记数器
int cnt,cntb;
//矩阵存储图
vector<int> edge[101];
//记录强连通分量
vector<int> connections[101];
//是否在栈中
bool inSta[101];
//时间戳
int dfn[101];
//回溯值
int low[101];
//栈
stack<int> s;void getConn(int u) {++cnt;//前序位置记录节点的时间戳和回溯值dfn[u]=low[u]=cnt;//入栈s.push(u);inSta[u]=true;//遍历子节点for(int i=0; i<edge[u].size(); ++i) {int v=edge[u][i];if(!dfn[v]) {//没有被访问getConn(v);//回溯位置,根据子节点的 low 更新父节点的 lovwlow[u]=min(low[u],low[v]);} else if(inSta[v])//访问过且在栈中,遇到了回边。更新 low 为祖先节点的时间戳low[u]=min(low[u],dfn[v]);}//后序遍历位置if(dfn[u]==low[u]) {//如果时间戳和回溯值相同,找到一条强连通分量++cntb;int t;do {t=s.top();s.pop();inSta[t]=false;connections[cntb].push_back(t);} while(t!=u);}
}
int main() {cin>>n>>m;for(int i=1; i<=m; ++i) {int u,v;cin>>u>>v;edge[u].push_back(v);}getConn(1);for(int i=1; i<=cntb; ++i) {cout<<"conn: "<<i<<" : ";for(int j=0; j<connections[i].size(); ++j)cout<<connections[i][j]<<" ";cout<<endl;}return 0;
}
//测试数据
//7 11
//1 2
//2 3
//2 5
//2 4
//3 5
//3 7
//7 5
//5 6
//6 7
//4 1
//4 5
思考一下,如下图的强连通分量有几个。
答案:有两个,分别是
6 5 4 3 2
1
3. 总结
强连通分量算法还有Kosaraju 、Garbow
算法。有兴趣者可自行了解。
相关文章:
C++图论之强连通图
1. 连通性 什么是连通性? 连通,字面而言,类似于自来水管道中的水流,如果水能从某一个地点畅通流到另一个地点,说明两点之间是连通的。也说明水管具有连通性,图中即如此。 无向图和有向图的连通概念稍有差…...
SadTalker数字人增加视频输出mp4质量精度
最近在用数字人简易方案,看到了sadtalker虽然效果差,但是可以作为一个快速方案,没有安装sd的版本,随便找了个一键安装包 设置如上 使用倒是非常简单,但是出现一个问题,就是输出的mp4都出马赛克了 界面上却…...
swing快速入门(三十二)消息对话框
注释很详细,直接上代码 上一篇 新增内容 1.自定义对话框前列图标 2.消息对话框的若干种形式 package swing21_30;import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent;public class swing_test_30 {// 定义一个JFrameJFrame jFrame n…...
《Spring Cloud学习笔记:Nacos配置管理 OpenFeign LoadBalancer Getway》
基于Feign的声明式远程调用(代码更优雅),用它来去代替我们之前的RestTemplate方式的远程调用 1. Nacos配置管理:Nacos Config 服务配置中心介绍 首先我们来看一下,微服务架构下关于配置文件的一些问题: 配置文件相…...
深入解析 Flink CDC 增量快照读取机制
一、Flink-CDC 1.x 痛点 Flink CDC 1.x 使用 Debezium 引擎集成来实现数据采集,支持全量加增量模式,确保数据的一致性。然而,这种集成存在一些痛点需要注意: 一致性通过加锁保证:在保证数据一致性时,Debez…...
060:vue中markdown编辑器mavon-editor的应用示例
第060个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下,本专栏提供行之有效的源代码示例和信息点介绍,做到灵活运用。 (1)提供vue2的一些基本操作:安装、引用,模板使…...
使用SCP在Linux中安全复制文件:参数详解
SCP(Secure Copy)是一个在Linux和其他类Unix系统中使用的命令行工具,用于在本地和远程主机之间安全地复制文件和目录。本文将详细介绍SCP的多个常用参数,并通过示例进行说明。 基本语法 scp [options] source destination其中&a…...
【动态规划精选题目】3、简单多状态模型
此动态规划系列主要讲解大约10个系列【后续持续更新】 本篇讲解简单多状态模型中的9道经典题,会在讲解题目同时给出AC代码 目录 1、按摩师 2、力扣198:打家劫舍1 3、打家劫舍II 4、删除并获得点数 5、 粉刷房子 6、力扣309:买卖股票的最佳时机含冷冻期 7、 买…...
软件测试/测试开发丨Python 虚拟环境及pip环境管理
venv 虚拟环境管理 venv 虚拟环境的优点 独立的 Python 环境,不会产生冲突有助于包的管理删除和卸载方便 venv 使用方法 创建虚拟环境 python3 -m venv test 激活虚拟环境 切换指定文件夹Windows:/Scripts/macOS:/bin/ 执行指令ÿ…...
Mybatis SQL构建器类 - SQL类
下面是一些例子: // Anonymous inner class public String deletePersonSql() {return new SQL() {{DELETE_FROM("PERSON");WHERE("ID #{id}");}}.toString(); }// Builder / Fluent style public String insertPersonSql() {String sql new…...
海云安亮相2023北京国际金融安全论坛,助力金融企业数字化转型降本增效
近日,2023北京国际金融安全论坛暨金融科技标准认证生态大会在北京金融安全产业园成功举办。深圳海云安网络安全技术有限公司(以下简称“海云安”)受邀参展亮相此次大会。海云安作为国内领先的金融科技服务商,展示了开发安全系列产…...
nodeJS搭建免费代理IP池爬取贴吧图片实战
之前用python写过爬虫,这次想试试nodeJS爬虫爬取贴吧图片,话不多说代码如下,爬取制定吧的前十页所有帖子里的图片 爬取贴吧图片脚本 你得提前创建一个images文件夹 const axios require("axios"); const cheerio require("…...
基于图搜索的自动驾驶规划算法 - BFS,Dijstra,A*
本文将讲解BFS,Dijstra,A*,动态规划的算法原理,不正之处望读者指正,希望有兴趣的读者能在评论区提出一些这些算法的面试考点,共同学习,一起进步 0 图论基础 图有三种:无向图、有向…...
Spring系列学习四、Spring数据访问
Spring数据访问 一、Spring中的JDBC模板介绍1、新建SpringBoot应用2、引入依赖:3、配置数据库连接,注入dbcTemplate对象,执行查询:4,测试验证: 二、整合MyBatis Plus1,在你的项目中添加MyBatis …...
HBase 创建不分裂的表 ( 禁止 Table Split )
注意:由于 HBase 版本众多,配置表的语法在不同版本上会有差异,本文介绍的配置方法是在 1.4.9 版本上测试的,使用 HBase 2.0 的版本需要核实并修改相关配置方法! 有时候,出于特殊需要,我们希望对…...
docker入门概念详解
本篇文章对docker的一些基础概念和周边概念进行了详细解释。帮助你可以很好的理解docker是用来干什么的,docker是怎么工作的。其中有docker所运用到的技术解释,docker的不同发展版本,dokcer的架构,docker的生态等等详解。希望本片…...
C++程序设计实践报告【格式】
C程序设计实践报告 原XX工业学院 C程序设计实践报告 题目: 专业: 学号: 姓名: 年 月 日 目录 一、绪…...
浅谈数据仓库运营
一、背景 企业每天都会产生大量的数据,随着时间增长,数据会呈现几何增长,尤其在系统基建基础好的公司。好的数据仓库需要提前规划和好的运营,才能支持企业的发展,为企业提供数据分析基础。 二、目标 提高数据仓库存储…...
系列六、Consul
一、Consul 1.1、概述 Consul是一套开源的分布式服务发现和配置管理系统,由HashiCorp公司用Go语言开发。他提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个功能都可以单独使用,也可以一起使用以构建全方位的服务网格&…...
Java集合/泛型篇----第一篇
系列文章目录 文章目录 系列文章目录前言一、ArrayList和linkedList的区别二、HashMap和HashTable的区别三、Collection包结构,与Collections的区别四、泛型常用特点前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站…...
集合使用注意事项
集合使用注意事项总结 集合判空 判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size()0 的方式 这是因为 isEmpty() 方法的可读性更好,并且时间复杂度为 O(1)。 集合转 Map 在使用 java.util.stream.Collectors 类的 toMap()…...
什么是 JavaScript 中的 WeakMap
在 JavaScript 中,WeakMap 是一种特殊的 Map 数据结构,它允许将对象作为键,而且键值对是弱引用的关系。 与 Map 不同的是,WeakMap 的键只能是对象,不能是其他类型的值。同时,当键对象没有任何引用时&#…...
nodejs+vue+ElementUi农产品团购销售系统zto2c
目标是为了完成小区团购平台的设计和实现,在疫情当下的环境,方便小区业主购入生活所需,减小居民的生活压力 采用B/S模式架构系统,开发简单,只需要连接网络即可登录本系统,不需要安装任何客户端。开发工具采…...
nacos入门篇001-安装与启动
1、下载zip包 我这里下载的是版本2.2.0 Nacos 快速开始 2、修改配置文件 2.1集群模式修改成单例模式 vi startup.sh 2.2 修改数据库配置信息 3、初始化数据库 3.1 创建db名称:db_nacos 3.2 执行mysql-schema.sql 3.3 执行完截图: 4、运行脚本启动 …...
WordPress主题大前端DUX v8.3源码下载
DUX主题8.3版本更新内容: 新增:Cloudflare Turnstile 免费验证功能 新增:子菜单页面模版,支持多级页面 新增:手机端文章内表格自动出现横向滚动条,可集体或单独设置滚动宽度 新增:标签云页面模版…...
RabbitMQ之快速入门、上手
前言 学习一样新技术、新框架,最重要的是学习其思想、原理。即原理性思维。 如果是因为工作原因,需要快速上手RabbitMQ,本篇或许适合你。 核心概念 Connection:publisher/consumer 和 broker 之间的 TCP 连接Channel…...
GBASE南大通用-GBase 8s数据库日志模式及切换
一、 GBase 8s数据库共有以下 4 种日志模式:无日志模式、缓冲日志模式、无缓冲日志模式、ANSI 模式。详细介绍如下: 1、无日志模式(Non logging): 采用无日志模式时,所有 DML 操作都不会被记录到日志中&…...
侵入式和非侵入式微服务框架的比较
微服务框架可以分为侵入式和非侵入式两种。侵入式框架需要对现有代码进行改造,而非侵入式框架则无需改造现有代码。 侵入式框架 侵入式框架将微服务治理功能嵌入到应用程序中,需要修改应用程序的代码。这种框架的优点是可以提供更强大的功能࿰…...
Go语言程序设计-第5章--函数
Go语言程序设计-第5章–函数 5.1 函数声明 每个函数声明都包含一个名字、一个形参列表、一个可选的返回列表以及函数体: func name(parameter-list) (result-list) {body }func add(x int, y int) int { return x y} func sub(x, y int) (z int) {z x - y; return} func f…...
数据被锁?被.mkp 勒索病毒攻击后的拯救行动
导言: 网络安全面临着越来越多的挑战,而.mallox勒索病毒则成为数字威胁中的一股强大势力。它的威胁不仅体现在其高度复杂的加密算法上,还表现在对受感染系统的深度渗透和数据的极大破坏上。以下是.mallox勒索病毒的主要威胁:如不…...
微商的自己做网站叫什么名字/网站要怎么创建
如果要获取行,则需要从每个数组中获取值,然后根据值创建新数组。您可以手动分配值,也可以使用for循环,例如...int[][] MyMat {{0,1,2,3,4}, {9,8,7,6,5}};// get your columns... (easy)int[] My0 MyMat[0]; //My0 {0,1,2,3,4}i…...
充值网站制作/最新中高风险地区名单
转载请注明出处:http://blog.csdn.net/u011733020前言:在Android开发中,对于图片的加载可以说是个老生常谈的问题了,图片加载是一个比较坑的地方,处理不好,会有各种奇怪的问题,比如 加载导致界面…...
wordpress默认链接/淘大象关键词排名查询
Hive支持的数据类型如下: 原生类型: TINYINTSMALLINTINTBIGINTBOOLEANFLOATDOUBLESTRINGBINARY (Hive 0.8.0以上才可用)TIMESTAMP (Hive 0.8.0以上才可用) 复合类型: arrays: ARRAY<data_type>maps: MAP<primitive_type, data_type>structs: STRUCT…...
手机端网站图片上传如何做/南京seo新浪
前言 III族一定是受主吗,V族一定做施主吗? 元素周期表 \;\\\;\\\; III族元素:硼B、铝Al、镓Ga、铟In V族元素:氮N、磷P、砷As、锑Sb...
万宁网站建设/b站推广app大全
shell 在计算机科学中,Shell俗称壳(用来区别于核),是指“提供使用者使用界面”的软件(命令解析器)。它类似于DOS下的command和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。 同时它又…...
有赞网站开发/推广app最快的方法
Feign简介 Feign是Netflix开发的声明式,模板化的HTTP客户端,其灵感来自Retrofit,JAXRS-2.0以及WebSocket.Feign可帮助我们更加便捷,优雅的调用HTTP API。在SpringCloud中,使用Feign非常简单——创建一个接口,并在接口上…...