SCC算法求强连通分量简单讲解证明及实现

article/2025/9/11 11:03:06

目录

  • 强连通分量
  • SCC算法简介
  • 两个概念
    • dfs结束时间
    • 转置图
  • SCC算法伪代码描述
  • SCC算法正确性证明
    • 引理1:
    • 引理2:
    • SCC证明
      • 不错找
      • 不漏找
  • 代码实现

强连通分量

连通分量要求任意两点可达,而强连通分量要求任意两点互相可达,即必须存在a->b且b->a的路径

强连通分量问题就是求解一个图中所有的强连通分量集合。

SCC算法简介

SCC算法在《算法导论》中有介绍到,SCC算法基于dfs,通过两次dfs可以求出图中所有的连通分量,是快速的方法。

在了解SCC算法之前先介绍两个概念

两个概念

dfs结束时间

规定一种【结束时间】,表示dfs退栈时候的时间,我们可以用两个数字表示dfs进栈,退栈的时间。如下图所示,左边的数字是dfs开始时间,右边的数字是dfs结束时间。
在这里插入图片描述
dfs结束时间可以表示拓扑排序的序列,即结束时间大的,拓扑排序排在结束时间小的节点前面,本质上表示退栈的先后,即访问顺序

转置图

即有向图的出边改为入边
在这里插入图片描述

SCC算法伪代码描述

对原图dfs并且将节点按照【结束时间】从大到小排序形成序列seq[]
按照seq[]序列里面的节点顺序 对【转置图】dfs 搜到的每一个连通分支就是一个强连通分量

?当我打出❓的时候,

这也太抽象了吧,所以下面给出证明

SCC算法正确性证明

证明过程需要一些引理的帮助,下面介绍几个引理

引理1:

假设有两个强连通分量c1和c2,并且存在从c1到c2的边,那么一定不存在从c2到c1的边。

在这里插入图片描述
证明很简单,如果存在边从c2到c1,那么c1c2就是同一个强连通分量,而不是两个强连通分量了。

引理2:

如果c1到c2有边,那么强连通分支【c1中最晚结束节点】的结束时间,一定晚于【c2中最晚结束节点】的时间。

在正向图中,c1的结束时间一定晚于c2,那么有:
在转置图中,c2的结束时间一定晚于c1

原因也很简单,因为c1一定先于c2被访问,毕竟要到达c2必先经过c1,转置图相当于边全反过来,所以存在边从c2到c1,而由引理1,不存在边从c1到c2了,同理可得c2的结束时间晚于c1。

SCC证明

假设原图中有强连通分量c1,在转置图上按照【原图中dfs结束时间从大到小】顺序dfs,一定能够找到c1内的所有点。并且不会找到其他强连通分支内

不错找

先来证明在反向图上对c1中的点dfs,不会找到其他分支内的点,即不错找

反证法:假设原图有强连通分支c1和c2,而我们在转置图中对c1内的点dfs,能够找到c2内的点,这表明转置图中有边c1到c2,也就是说原图中有边c2到c1,那么说明c2所有点的结束时间晚于c1中的所有点,那么在转置图的dfs中,c2的点一定先于c1的点被dfs到,所有c1的点永远到不了c2的点,即不会【错找】
在这里插入图片描述

不漏找

这个好理解,假设存在x->y表示x到y有路径

强连通分量中,存在a->b和b->a,那么将边全部反向变成转置图之后

  1. a->b(原) 变为 b->a(新)
  2. b->a(原) 变为 a->b(新)

仍然存在存在 a->b和b->a,是强连通,不漏找

代码实现

ps:其实第一次dfs,退栈之后的节点装到一个栈里面。dfs结束之后,按照顺序弹出节点,就是结束时间从晚到早的排序序列

#include <bits/stdc++.h>using namespace std;int n, e, ans=0;
vector<vector<int>> adj;
vector<vector<int>> adj_T;	// 转置图 
vector<int> vis;
vector<int> seq;	// 存节点 下标越大 结束时间越晚// dfs原图 
void dfs(int x)
{vis[x] = 1;for(int i=0; i<adj[x].size(); i++)if(vis[adj[x][i]]==0) dfs(adj[x][i]);seq.push_back(x);
}// dfs转置图 
void dfs_T(int x)
{vis[x] = 1;for(int i=0; i<adj_T[x].size(); i++)if(vis[adj_T[x][i]]==0) dfs_T(adj_T[x][i]);
}int main()
{ 	cin>>n>>e;adj.resize(n); adj_T.resize(n); vis.resize(n);for(int i=0; i<e; i++){int st, ed; cin>>st>>ed;adj[st].push_back(ed);adj_T[ed].push_back(st);}// dfs原图 for(int i=0; i<n; i++) if(vis[i]==0) dfs(i);// 重置vis for(int i=0; i<n; i++) vis[i]=0;// 按照seq的顺序dfs转置图 for(int i=n-1; i>=0; i--) if(vis[seq[i]]==0) dfs_T(seq[i]),++ans; cout<<"强连通分量个数: "<<ans<<endl;return 0;
}/*
6 8
0 1
0 4
1 2
2 0
2 1
3 2
4 5
5 4
*/

http://chatgpt.dhexx.cn/article/hRdNsp7c.shtml

相关文章

最长公共子串LCS (Longest Common Subsequence) 算法

三个方法都有所借鉴&#xff0c;但代码部分是自己试着写出来的&#xff0c;虽然最后的运行结果都是正确的&#xff0c;但此过程中难免会有考虑不周全的地方&#xff0c;如发现代码某些地方有误&#xff0c;欢迎指正。同时有新的想法&#xff0c;也可以提出! 采用顺序结构存储串…

常考的经典算法--最长公共子序列(LCS)与最长公共子串(DP)

《1》最长公共子序列&#xff08;LCS&#xff09;与最长公共子串(DP) http://blog.csdn.net/u012102306/article/details/53184446 https://segmentfault.com/a/1190000007963594 http://www.cppblog.com/mysileng/archive/2013/05/14/200265.html 1. 问题描述 子串应该比…

LCS算法学习

LCS的定义 最长公共子序列&#xff0c;即Longest Common Subsequence&#xff0c;LCS。一个序列S任意删除若干个字符得到新序列T&#xff0c;则T叫做S的子序列&#xff1b;两个序列X和Y的公共子序列中&#xff0c;长度最长的那个&#xff0c;定义为X和Y的最长公共子序列。 字符…

算法系列之六:最长公共子序列(LCS)问题(连续子序列)的三种解法

最长公共子序列&#xff08;LCS&#xff09;问题有两种方式定义子序列&#xff0c;一种是子序列不要求不连续&#xff0c;一种是子序列必须连续。上一章介绍了用两种算法解决子序列不要求连续的最终公共子序列问题&#xff0c;本章将介绍要求子序列必须是连续的情况下如何用算法…

Hirschberg的LCS算法实现

解决Longest Common Subsequence(LCS)问题最常用的算法是Dyanmic programing&#xff0c;细节可以参考Ch15.4 of Introduction of Algorithm(2ED), MIT press, p 350。这个算法最大的问题是他的空间复杂度是O(m*n)。这样&#xff0c;当两个序列达到上万个节点时&#xff0c;内存…

SLIC算法

基础知识 在介绍SLIC之前&#xff0c;先来介绍以下Lab颜色空间的介绍。 Lab色彩模型是由亮度(L)要素和与有关色彩的a,b要素组成&#xff0c;L的值由0(黑色)到100(白色)&#xff0c;a表示从洋红色至绿色的范围(a为负值表示绿色而正值表示品红)&#xff0c;b表示从黄色至蓝色的…

动态规划之LCS算法

一、前言 LCS是Longest Common Subsequence的缩写&#xff0c;即最长公共子序列。一个序列&#xff0c;如果是两个或多个已知序列的子序列&#xff0c;且是所有子序列中最长的&#xff0c;则为最长公共子序列。 另外还有个分支问题&#xff1a;最长公共子串。子串的字符位置必…

LCS算法的C++实现

这两天忙里偷闲看了July的团队提供的LCS算法视频&#xff0c;真的如视频标题一样&#xff0c;十分钟搞定LCS算法。 感谢July大神&#xff0c;感谢其团队的邹博。 这里附上视频链接&#xff1a;http://www.julyedu.com/video/play?course17 说是十分钟搞定&#xff0c;其实是…

算法学习 - 最长公共子序列(LCS)C++实现

最长公共子序列 最长公共子序列的问题很简单&#xff0c;就是在两个字符串中找到最长的子序列&#xff0c;这里明确两个含义&#xff1a; 子串&#xff1a;表示连续的一串字符 。子序列&#xff1a;表示不连续的一串字符。 所以这里要查找的是不连续的最长子序列&#xff0c; …

SLIC算法介绍

SLIC&#xff08;simple linear iterativeclustering&#xff09;&#xff0c;即 简单线性迭代聚类 。 &#x1f49b;它是2010年提出的一种思想简单、实现方便的算法&#xff0c;将彩色图像转化为CIELAB颜色空间和XY坐标下的5维特征向量&#xff0c;然后对5维特征向量构造距离度…

LSC算法

1.问题 给定序列 X<x_1,x_2,…,x_m> Y<y_1,y_2,…,y_j> 求X和Y的最长公共子序列(LCS) 2.解析 X<x1,x2,x3,x4…,xi> Y<y1,y2,y3,y4…,yi> 如果Z<z1,z2,z3,z4…,zk>是他们的最长公共子序列 则&#xff1a; &#xff08;1&#xff09;xi yi&…

LCS算法详解

程序员编程艺术第十一章&#xff1a;最长公共子序列(LCS)问题 0、前言 程序员编程艺术系列重新开始创作了&#xff08;前十章&#xff0c;请参考程序员编程艺术第一~十章集锦与总结&#xff09;。回顾之前的前十章&#xff0c;有些代码是值得商榷的&#xff0c;因当时的代码只顾…

LCS 最大公共序列算法

这些天在了解chrome的courgette, 了解了rsync算法, 也了解了courgette使用了bsdiff 算法, 然后知道了bsdiff算法里主要使用的是 LCS 算法, 这里参考了july大牛的文章: http://blog.csdn.net/v_july_v/article/details/6695482 自己做一点概括性的总结, 用以备忘, 也把自…

最长公共子序列(LCS)算法

一、最长公共字串与最长公共子序列 最长公共子串&#xff08;Longest Common Substirng&#xff09; 子串是串的一个连续的部分&#xff0c;子串中字符的位置必须连续。 例如&#xff1a;有两个字符串ABCBDAB 和 BDCABA&#xff0c;则它们的最长公共子串是&#xff1a;AB。 …

LCS(longest common sequence)算法的实现(十分详细)

一、问题描述 有两个字符串&#xff0c;求二者的最长公共子序列。 最长公共子序列&#xff1a;不必连续但必须有序的子列&#xff08;与子串区分&#xff0c;子串是连续的&#xff09; 二&#xff1a;解决方法 第一种方法&#xff1a;穷举法 &#xff0c;就是一个一个的对比&a…

LCS算法

LCS算法 LCS算法&#xff1a; LCS是Longest Common Subsequence的缩写&#xff0c;即最长公共子序列。一个序列&#xff0c;如果是两个或多个已知序列的子序列&#xff0c;且是所有子序列中最长的&#xff0c;则为最长公共子序列。LCS不是唯一的&#xff0c;它可以有很多种&am…

Oracle中索引的原理

索引的概念 索引是一种数据库结构&#xff0c;能够就数据库中的某列提供快速查询&#xff0c;而不用检索整个表格&#xff08;官方的不行&#xff09;。 在 Oracle 数据库中&#xff0c;存储的每一行数据都有一个 rowID 来标识。当 Oracle 中存储着大量的数据时&#xff0c;意…

MongoDB索引原理及实践

背景 数据库的演进 随着计算机的发展&#xff0c;越来越多的数据需要被处理&#xff0c;数据库是为处理数据而产生。从概念上来说&#xff0c;数据库是指以一定的方式存储到一起&#xff0c;能为多个用户共享&#xff0c;具有更可能小的冗余&#xff0c;与应用程序彼此独立的…

MySql存储引擎和索引原理

转载 https://blog.csdn.net/tongdanping/article/details/79878302 注意&#xff1a; 1、索引需要占用磁盘空间&#xff0c;因此在创建索引时要考虑到磁盘空间是否足够 2、创建索引时需要对表加锁&#xff0c;因此实际操作中需要在业务空闲期间进行 MySQL支持诸多存储引擎&a…

MySQL之索引原理

1 简介 索引底层就是一种数据结构&#xff0c;空间换时间&#xff0c;能够帮助我们快速定位到对应的数据&#xff0c;就类似于字典里面的目录一样。 索引虽然能快速检索数据&#xff0c;但会影响数据修改的操作&#xff0c;而且索引存储在具体的文件&#xff0c;占用一定的空…