在上一节我们已经知道tarjan算法可以求联通图,在这里我们也运用tarjan的思想求割点与割边,首先我们先来说说割点,那么什么事割点呢,先来看一张图(a),图片来自网络

在(a)图中,我们将A点以及与A点相连的边全部去除,会发现这个联通图被分成了俩个联通图,一个是节点F,另外一个是余下的所有的节点组成的图,因此我们将A点称为割点,同理我们发现B点也是割点,因此我们可以这样定义割点,在一个无向联通图中,如果删除某个节点后,图不再连通(即任意俩点之间不能相互到达),我们称这样的顶点为割点(定义来自啊哈算法),那么问题来了,如何求割点呢???

很容易的一个想法就是我们每次删除一个节点后用bfs或者dfs来遍历图是否依然联通,如果不联通则该点是割点,这种方法的复杂度是O(N(N + M)),太暴力了,我们来想想其他的方法

再介绍其他方法前,我们再来了解几个定义(定义来源于网络)

  • DFS搜索树:用DFS对图进行遍历时,按照遍历次序的不同,我们可以得到一棵DFS搜索树,如图(b)所示。
  • 树边:(在[2]中称为父子边),在搜索树中的实线所示,可理解为在DFS过程中访问未访问节点时所经过的边。
  • 回边:(在[2]中称为返祖边后向边),在搜索树中的虚线所示,可理解为在DFS过程中遇到已访问节点时所经过的边。

通过观察图(2),我们发现有俩类节点可能成为割点

  1. 对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点;(说明删除该节点会产生至少俩棵不联通的子树)
  2. 对非叶子节点u(非根节点),若其子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与u的子树的节点不再连通;则节点u为割点。(在这里可以简单画个图理解一下)

对于这俩类节点,前者我们很容易判断,我们就不做过多的说明了,具体的判断会在代码里面讲解,我们来说说后者的判断

我们用dfn[u]记录节点u在DFS过程中被遍历到的次序号,low[u]记录节点u或u的子树通过非父子边追溯到最早的祖先节点(即DFS次序号最小),那么low[u]的计算过程如下:

low[u]={min{low[u], low[v]},((u,v)为树边)

low【u】 = min{low[u], dfn[v]}    (u,v)为回边且v不为u的父亲节点

至于为什么不为u的父亲节点我暂时还没有想明白......

关于dfn和low数组的计算请参考我另外一篇博客或者去百度上查询资料,这个不好用语言描述,我就暂且大家都知道了

那么对于第二类节点,当(u,v)为树边且low[v] >= dfn[u]时,节点u才为割点。该式子的含义:表示v节点不能绕过u节点从而返回到更早的祖先,因此u节点为割点

下面来看代码实现,在代码里面会做详细的解说

#include<iostream>
#include<cmath>
using namespace std;
int n, m, root, a, b, total;
int e[101][101], dfn[101], low[101], flag[101], head[101];//链式前向星,不懂的自行百度
struct node{int to;int next;
}edge[10010];int cnt = 1;//前向星建图
void add(int u, int v) {edge[cnt].to = v;edge[cnt].next = head[u];head[u] = cnt++;
}void tarjan(int u, int father) {int child = 0;//child用来记录在生成树中当前顶点的儿子的个数dfn[u] = low[u] = ++total;//时间戳for (int i = head[u]; i != 0; i = edge[i].next) {//前向星遍历该顶点的所有边int v = edge[i].to;//该顶点连接的顶点if (!dfn[v]) {//如果时间戳为0说明没有访问过child++;tarjan(v, u);//继续往下搜索low[u] = min(low[u], low[v]);//更新当前时间戳//  如果当前节点是根节点并且儿子个数大于等于2,则满足第一类节点,为割点if (u == root && child >= 2) {flag[u] = 1;//不为根结点但是满足第二类条件的节点} else if (u != root && low[v] >= dfn[u]) {flag[u] = 1;}//如果顶点被访问过并且不是该节点的父亲,说明此时的v为u的祖先,因此需要更新最早顶点的时间戳} else if (v != father) {low[u] = min(low[u], dfn[v]);}}
}int main() {cin >> n >> m;for (int i = 0; i < m; i++) {cin >> a >> b;add(a, b);add(b, a);}root = 1; //假设1为根节点
//从1号顶点开始进行深度优先搜索(tarjan)tarjan(1, root);for (int i = 1; i <= n; i++) {if(flag[i]) {cout << i << " ";}}return 0;
}

给一组数据拿来测试一下该代码

6 7(6个顶点,7条无向边)

1 4

1 3

4 2

3 2

2 5

2 6

5 6

输出为 2

如果有多组图,那么我们使用另外的一组代码,见下

#include<iostream>
using namespace std;
const int maxn = 20010;
const int maxv = 200010;
int cnt = 1, n, m, total, a, b;
int head[maxn], flag[maxn], dfn[maxn], low[maxn], sum;struct node{int to;int next;
}edge[maxv];void add(int u, int v) {edge[cnt].to = v;edge[cnt].next = head[u];head[u] = cnt++;
}void tarjan(int u, int father) {int child = 0;dfn[u] = low[u] = ++total;for (int i = head[u]; i != 0; i = edge[i].next) {int v = edge[i].to;if (!dfn[v]) {tarjan(v, father);low[u] = min(low[u], low[v]);if (low[v] >= dfn[u] && u != father) {flag[u] = 1;}if (u == father) child++;}low[u] = min(low[u], dfn[v]);}if (child >= 2 && u == father) {flag[u] = 1;}
}int main () {cin >> n >> m;for (int i = 0; i < m; i++) {cin >> a >> b;add(a, b);add(b, a);}for (int i = 1; i <= n; i++) {if (!dfn[i]) {tarjan(i, i);}}for (int i = 1; i <= n; i++) {if (flag[i]) sum++;}cout << sum << endl;for (int i = 1; i <= n; i++) {if (flag[i]) {cout << i << " ";}}return 0;
}

了解完割点后我们再来了解什么是割边,其实很简单,即在一个无向联通图中,如果删除某条边后,图不在联通,那么该条边称为割边,代码和上面的代码基本上一模一样,只要改一个小地方,就是将low【v】 >= dfn[u] 改为 low【v】> dfs

[u]即可,前者便是还可以回到父亲,后者表示连父亲都回不到了,倘若顶点v不能回到祖先,也没有另外一条路可以回到父亲,那么u-v这条边就是割边,其实仔细想想模拟个图就明白了,代码实现如下

#include<iostream>
#include<cmath>
using namespace std;
int n, m, root, a, b, total;
int e[101][101], dfn[101], low[101], flag[101], head[101];struct node{int to;int next;
}edge[10010];int cnt = 1;
void add(int u, int v) {edge[cnt].to = v;edge[cnt].next = head[u];head[u] = cnt++;
}void tarjan(int u, int father) {int child = 0;dfn[u] = low[u] = ++total;for (int i = head[u]; i != 0; i = edge[i].next) {int v = edge[i].to;if (!dfn[v]) {child++;tarjan(v, u);low[u] = min(low[u], low[v]);if  (low[v] > dfn[u]) {cout << u << "->" << v << endl;}} else if (v != father) {low[u] = min(low[u], dfn[v]);}}
}int main() {cin >> n >> m;for (int i = 0; i < m; i++) {cin >> a >> b;add(a, b);add(b, a);}root = 1;tarjan(1, root);for (int i = 1; i <= n; i++) {if(flag[i]) {cout << i << " ";}}return 0;
}

如果输入上面的那组数据那么不会有输出,因为没有割边,再给出一组数据

6 6

1 4

1 3

4 2

3 2

2 5

5 6

输出5->6

2->5

割点和割边就解释到这里了,完毕

tarjan算法求割点割边相关推荐

  1. Tarjan算法求无向图割边割点、最近公共祖先的总结

     无向图tarjan求割边割点.最近公共祖先总结 割点:删除这个点之后整个图变成不连通的两个部分的点 割点集合:在一个无向图中删除该集合中的所有点,能使原图变成互不相连的连通块的点的集合 点连通度 ...

  2. C++算法篇:DFS超详细解析(2)--- tarjan算法求无向图割边

    <<<上一篇 系列文章目录 ①:无向图基本概念 ②:tarjan算法求无向图割边 前言 第一次写算法,讲得肯不透彻,有误还请指教awa 文章目录 系列文章目录 一.回顾 二.tarj ...

  3. 海亮DAY8 关于Tarjan算法用于割点割边相关感受

    Tarjan 简介 Tarjan算法在求割点,割边,连通分量方面及其高效,在军事,交通,设计等方面有重要作用. 由于Tarjan算法思想并不难懂,在此不放上Tarjan算法的具体介绍. [Usaco2 ...

  4. Tarjan算法求割点与割边(python3实现)

    from typing import List, Tuple''' Trajan算法求无向图的桥 '''class Tarjan:# 求无向连通图的桥@staticmethoddef getCutti ...

  5. tarjan算法求无向图的割点和桥

    tarjan算法求无向图的割点与桥 一篇tarjan算法求割点与桥的完整的解释,写的真的好认真 以下代码来自kuangbin的模板 4.5 图的割点.桥和双连通分支的基本概念 [点连通度与边连通度] ...

  6. 算法提高课-图论-有向图的强连通分量-AcWing 1174. 受欢迎的牛:tarjan算法求强连通分量、tarjan算法板子、强连通图

    文章目录 题目解答 题目来源 题目解答 来源:acwing 分析: 强连通图:给定一张有向图.若对于图中任意两个结点x,y,既存在从x到y的路径,也存在从y到x的路径,则称该有向图是"强连通 ...

  7. tarjan算法求SCC,e-DCC,v-DCC

    在介绍算法之前先引入几个概念: 时间戳: 在深度优先搜索时,每个点xxx第一次被访问的顺序为时间戳,记作dfn[x]dfn[x]dfn[x](从111开始) 追溯值: 从xxx开始走,所能遍历到的最小 ...

  8. Tarjan算法求桥

    桥的定义: 在图论中,一条边被称为"桥"代表这条边一旦被删除,这张图的连通块数量会增加.等价地说,一条边是一座桥当且仅当这条边不在任何环上.一张图可以有零或多座桥. 理论: 从某个 ...

  9. SPF Tarjan算法求无向图割点(关节点)入门题

    SPF 题目抽象,给出一个连通图的一些边,求关节点.以及每个关节点分出的连通分量的个数 邻接矩阵只要16ms,而邻接表却要32ms,  花费了大量的时间在加边上. //   time  16ms 1 ...

最新文章

  1. 微信小程序制作-随笔2
  2. linux内存转换成功,linux系统内存转换成硬盘使用
  3. mysql数据库限制多次登录失败,限定用户重试时间
  4. 剑指offer 面试64题
  5. html 未来元素绑定事件,jquery on如何给未来元素绑定事件?
  6. mysql自动编号_MySQL自动编号与主键
  7. stm32f102 SPI口重复初始化引起的问题及解决办法
  8. eclipse查看git地址_Git大文件管理:函数计算和OSS支持的Git LFS服务器
  9. ThinkPHP之MVC简析
  10. 红河学院计算机科学与技术,2016年红河学院计算机科学与技术专业最低分是多少?...
  11. 人的“肥胖”基因FTO可促进水稻和土豆增产50% - 中国粮食、中国饭碗
  12. SPOJ DQUERY 求区间内不同数的个数 主席树
  13. 为什么你还一直在穷打工?
  14. tiny core linux 7.1,极度简约 最小 Linux 发行版 Tiny Core Linux 7.1 发布
  15. 5G终端天线设计,到底有多难?
  16. (七)linux操作系统-linux韩顺平2021笔记
  17. 流量转发的思路-软件流量转发 管家婆 客户端 端口 更改
  18. linux终端ANSI转义字符
  19. CentOS 6.4 安装D-Link 525(RT5360)无线网卡驱动
  20. a轮b轮c轮天使轮区别是什么?

热门文章

  1. 全新VR全景可视化制作小程序系统源码+公众号功能模块1.0.28
  2. Codeforces 600E Lomsat gelral 树上启发式合并,线段树合并.
  3. 标准日语初级 测试题
  4. 3D目标检测中点云的表征方式总结(一)
  5. html网页无法进入系统,怎样解决无法打开网页的问题
  6. ELK 7.17.5 集群部署及使用
  7. Unity2017五子棋大战_人机_双人_UNET联网
  8. Hadoop HA高可用集群搭建(Hadoop+Zookeeper+HBase)
  9. java工程师安卓方向_校招|vivo提前批JAVA工程师Android方向
  10. 计算机无线鼠标用不了怎么办,电脑的无线鼠标突然用不了了有什么方法解决