首先我们需要搞懂图论中的一些基础概念

完全图: 假设一个图有 n n n 个顶点, 并且每两个点之间都有边就叫完全图
连通图(多指无向图): 对于两个点, u , v , u,v, u,v, 如果 u , v u,v u,v 之间有通路,则称 u , v u,v u,v 两点连通, 如果图中任意两个点都连通, 则称这个图为连通图
连通分量: 连通分量中任意两点 u , v , u,v, u,v, 两两之间必定能互相到达, u − > v , v − > u . u->v, v->u. u−>v,v−>u. (当然一个点也是连通分量)
强连通分量: 极大的连通分量,对于连通分量内部的任意两个点 u , v , u, v, u,v,, 既存在 u u u 到 v v v 的路径, 又存在 v v v 到 u u u的路径,并且如果再加入其它点和边就不再连通的,


tarjan算法求强连通分量

了解 t a r j a n tarjan tarjan 算法之前你必须了解一个概念, 时间戳

时间戳: 根据深度优先搜索的顺序给节点标号
有了时间戳后我们就可以引入两个概念:
d f n [ u ] dfn[u] dfn[u]: 节点 u u u 所对应的时间戳
l o w [ u ] low[u] low[u]: 节点 u u u 所能够到达的时间戳的最小值

如果 d f n [ u ] = = l o w [ u ] dfn[u] == low[u] dfn[u]==low[u] ⟺ \iff ⟺ u u u 节点是所在强连通分量的最高点

判断一个点是否在某个强连通分量中 ⟺ \iff ⟺ 该点是否能走到该强连通分量的最高点

tarjan 算法求强连通分量的步骤

  1. 缩点
  2. 建图
  3. 求拓扑序(递推)

1.缩点

void tarjan(int u)
{dfn[u] = low[u] = ++ timestamp;  //分配编号s.push(u), is_stack[u] = true;  //入栈, 标记为在栈中for(int i = h[u]; ~i; i = ne[i]) //遍历每条邻边{int j = e[i];if(!dfn[j])  //如果这个点没有被搜索过{tarjan(j);  //搜到底low[u] = std :: min(low[u], low[j]);  //用j更新u}else if(is_stack[j])  low[u] = std :: min(low[u], dfn[j]); //如果这个点没被搜到过, 并且在栈中, 说明存在横叉边}if(dfn[u] == low[u])  //u点是所在强连通分量的最高点{int y;scc_cnt ++; //强联通分量数量++do {y = s.top(); //取出栈顶s.pop();size[scc_cnt] ++;  //该连通分量中点的数量++id[y] = scc_cnt; //分配编号is_stack[y] = false;  //标记为不在栈中}while(y != u);}
}

2.建图

for(int i = 1; i <= n; i ++ )for(int j = h[i]; ~j; j = ne[j]){int k = e[j];int a = id[i], b = id[k];if(a != b) add(hs,a, b);//hs为新图的头结点}

3.求拓扑序

for(int i = scc_cnt; i; i -- )

因为 t a r j a n tarjan tarjan 算法最后求出来的就是逆拓扑序, 所以不用再进行拓扑排序了

无向图的双连通分量
1.边的双连通分量 e − d c c e-dcc e−dcc 极大的不包含桥的连通块
2.点的双连通分量 v − d c c v-dcc v−dcc 极大的不包含割点的连通块

首先要知道桥是什么?
就是去掉这条边后, 能使连通块数量增加的边(或者说使得这个图不连通)
割点 就是去掉这个点和它所关联的边,能使得连通块数量增加的点(或者说使得这个图不连通)

点双连通分量的特殊性质
除了只包含两个点的点双连通分量外, 其他点双连通分量都满足一个性质:
任 意 两 点 间 都 存 在 不 相 交 的 两 条 路 径 任意两点间都存在不相交的两条路径 任意两点间都存在不相交的两条路径

任何一个割点都存在于两个点双连通分量中
因为删去割点后, 图就不连通了, 所以割点连接的一定是不相同的两个点双连通分量

任意一个非割点都只存在于一个点双连通分量中

节点 1 , 6 1,6 1,6 是割点, 右边深色的是四个边双连通分量,可以看出每个割点都位于两个边双连通分量中

t a r j a n tarjan tarjan 求割点

割点的定义是删去这个点后图不连通, 那么肯定存在一些点,在不经过割点的情况下, 无法到达割点的祖先,所以我们可以通过这个性质去寻找割点

有割点 ⟺ \iff ⟺ l o w [ y ] ≥ d f n [ x ] low[y] ≥ dfn[x] low[y]≥dfn[x]
这是什么意思呢? 就是说节点 y y y 能够到达的最高点的时间戳比 x x x 的时间戳要大, 说明 节点 y y y无法到达 节点 x x x 的祖先, 所以如果删除节点 x x x, 就不连通了
这里存在两种情况:

  1. 如果 x x x 不是根节点, 那么只要 l o w [ y ] ≥ d f n [ x ] low[y] ≥ dfn[x] low[y]≥dfn[x] , x 必然是割点
  2. 如果 x x x 是根节点, 如果只有一个子节点, 删去 x x x及与 x x x 相关联的边后, 图还是连通的, 不满足割点定义, 所以如果 x x x 是根节点, 还需要 x x x 有两个子节点

为了求出"点双连通分量", 需要在 t a r j a n tarjan tarjan 算法的过程中维护一个栈, 并按照如下方法维护栈中的元素:

  1. 当一个节点第一次访问时,把该节点入栈
  2. 当割点判定法则中的条件 l o w [ y ] ≥ d f n [ x ] low[y] ≥ dfn[x] low[y]≥dfn[x] 成立时, 无论 x x x 是否为根, 都要:
    (1):从栈顶不断弹出节点, 直至节点 y y y 被弹出
    (2): 刚才弹出的所有节点与节点 x x x 一起构成一个 v − D C C v-DCC v−DCC(点双连通分量);
    模板:
void tarjan(int u)
{dfn[u] = low[u] = ++ timestamp; // 分配时间戳stk[++ tt] = u; //入栈if(u == root && h[u] != -1){dcc_cnt ++;dcc[dcc_cnt].push_back(u);return;}int cnt = 0; //记录节点u下, 子树的数量for(int i = h[u]; ~i; i = ne[i]){int j = e[i];if(!dfn[j]){tarjan(j);low[u] = min(low[u], low[j]);if(low[y] >= dfn[x]) {cnt ++;if(u != root || cnt > 1) cut[u] = true; //如果是子树大于1, 或者是根节点,那么这个点就是割点dcc_cnt ++; int y;do{y = stk[tt --];dcc[dcc_cnt].push_back(y);}while(y != j);  //到j就退出了, 此时u还未被弹出//u作为割点既可以和这个组成v-dcc也可以和另一个组成v-dccdcc[dcc_cnt].push_back(u);}else low[u] = min(low[u], dfn[j]);}}
}

边的双连通分量

要求边的双连通分量首先需要找到 ,
x x x 与 y y y 之间是桥 ⟺ \iff ⟺ l o w [ y ] > d f n [ x ] low[y] > dfn[x] low[y]>dfn[x]

你会发现这和割点判断的很相似, 我们不妨来梳理一下

l o w [ u ] = = d f n [ u ] low[u] == dfn[u] low[u]==dfn[u] ⟺ \iff ⟺ u u u点是强连通分量的最高点
l o w [ y ] ≥ d f n [ x ] low[y] ≥ dfn[x] low[y]≥dfn[x] ⟺ \iff ⟺ x x x是割点
l o w [ y ] > d f n [ x ] low[y] > dfn[x] low[y]>dfn[x] ⟺ \iff ⟺ x x x 和 y y y 之间有桥

如何找到所有的边的双连通分量 ?
因为边的双连通分量是不包含桥的极大连通分量, 所以我们必须将所有的桥删掉

void tarjan(int u, int fa)
{dfn[u] = low[u] = ++ timestamp;  //分配时间戳stk[++ tt] = u;  //入栈for(int i = h[u]; ~i; i = ne[i])  //遍历每条邻边{int j = e[i];if(!dfn[j]){tarjan(j, i); //搜到底low[u] = min(low[j], low[u]);  //用j更新uif(low[j] > dfn[u]) //判定为桥is_bridge[i] = is_bridge[i ^ 1] = true;}//如果走到了j, 并且j不是由fa的反向边走过来的 else if(i != (fa ^ 1)) low[u] = min(low[u], dfn[j]);}if(dfn[u] == low[u]) //走到了该连通分量的最高点{int y;++dcc_cnt;do{y = stk[tt -- ];  //取出id[y] = dcc_cnt; //分配编号}while(y != u);}
}

完结✿✿ヽ(°▽°)ノ✿! 给个赞吧!

有向图的强连通分量与无向图的双连通分量总结相关推荐

  1. 无向图的双连通分量(一)

    目录 无向图的双连通分量 概念 ①边双连通分量 e-DCC 桥: ②点双连通分量 v-DCC 割点: e-DCC Tarjan算法 判断当前边是否为桥: 寻找e-DCC: 例题 代码 tarjan算法 ...

  2. 关于有向图强连通分量 和 无向图双联通分量的理解

    有向图的强连通分量 1.强连通 代表的是 这个连通块中的每两个点互相都是有一条路径是可以走到的 2.分量 就是子图: 从这张图上可以看出 A B C这三个点都是互相可以走到的 所以他们就是一个联通块 ...

  3. 求无向图的连通分量或有向图的强连通分量—tarjan()ccf高速公路

    概念定义: 在图论中,连通图基于连通的概念. 1. 连通(无向图): 若顶点Vi能通过路径到达Vj,那么称为Vi和Vj是连通的 对无向图:若从顶点Vi到顶点Vj有路径相连(当然从j到i也一定有路径), ...

  4. 使用并查集实现查找无向图的连通分量和求解有向图的强连通分量

    目录 1.无向图的连通分量 2.求解连通分量算法的实现 3.有向图的强连通分量 4.求解有向图的强连通分量 使用C语言实现并查集 有向图和无向图 1.无向图的连通分量 无向图G中,如果存在从顶点v1到 ...

  5. Tarjan 求有向图的强连通分量

    Tarjan 算法与有向图的连通性 Tarjan 算法是基于对图进行深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树.搜索时,把当前搜索树中未处理的节点加入一个栈,回溯时可以判断栈顶到栈中的节点 ...

  6. 缩点(有向图的强连通分量)学习笔记

    缩点(有向图的强连通分量)学习笔记 1.什么是强连通分量?: 有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路 ...

  7. 有向图的强连通分量,割点与桥

    有向图的强连通分量 1.Tarjan /* Tarjan算法 复杂度O(N+M) */ #include<iostream> #include<stdio.h> #includ ...

  8. 【图论】—— 有向图的强连通分量

    给定有向图 ,若存在 ,满足从 出发能到达 中所有的点,则称 是一个"流图"( Flow Graph ),记为  ,其中, 称为流图的源点. 在一个流图  上从  进行深度优先遍历 ...

  9. 算法提高课-图论-有向图的强连通分量-AcWing 367. 学校网络:强连通分量、tarjan算法

    文章目录 题目解答 题目来源 题目解答 来源:acwing 分析: 第一问:通过tarjan算法求出强连通分量并且缩点后,统计入度为0的点的个数p即可. 第二问,至少加几条边才能使图变成强连通分量?这 ...

最新文章

  1. 2021年高考,26地公布高考出分时间
  2. 8-12-COMPETITION
  3. zookeeper可视化工具zkui
  4. ThinkPad紧凑型蓝牙键盘(0B47189)鼠标滚轮用法,F1到F12功能键的功能切换以及其他技巧
  5. .NET Worker Service 作为 Windows 服务运行及优雅退出改进
  6. mysql和sqlserver分页的区别_关于SQLServer和MySQL 查询分页语句区别
  7. Android 2017-2018面试题详解
  8. MySQL/sqlserver查询in操作 查询结果按in集合顺序显示
  9. stm32 PWM输入捕获
  10. 扩展欧几里得算法与模乘逆元的程序
  11. python占位符_Python(2)——Python的基础语句
  12. svg文件解析(python)
  13. 用户权限集中管理方案
  14. 【MySQL】MySQL用 limit 为什么会影响性能?
  15. NVIDA和AMD天梯图
  16. unity字体外发光_【Unity学习心得】Sprite外发光的制作
  17. 程序员找工作时的技巧
  18. 基于路灯控制系统的线缆防盗方案
  19. openpose的细节处理
  20. 学生管理系统登录界面/接口---简单前后端连接

热门文章

  1. 谷歌浏览器F12 后 ,开发者模式不正常
  2. TransformerCPI文献阅读
  3. web漏洞“小迪安全课堂笔记”文件操作安全,文件包含
  4. 说都不会话了!147条真理!
  5. stty命令用法(设置串口)
  6. layer.js确认框
  7. jxl 创建表格(包括去掉网格线、字体设置、单元格设置、对齐方式等设置)
  8. ROS编程(一)写一个最简单的发布者订阅者
  9. c#数组转换成字符串
  10. MATLAB之傅里叶变换,快速傅里叶变换FFT