tarjan算法求割点割边
在上一节我们已经知道tarjan算法可以求联通图,在这里我们也运用tarjan的思想求割点与割边,首先我们先来说说割点,那么什么事割点呢,先来看一张图(a),图片来自网络
在(a)图中,我们将A点以及与A点相连的边全部去除,会发现这个联通图被分成了俩个联通图,一个是节点F,另外一个是余下的所有的节点组成的图,因此我们将A点称为割点,同理我们发现B点也是割点,因此我们可以这样定义割点,在一个无向联通图中,如果删除某个节点后,图不再连通(即任意俩点之间不能相互到达),我们称这样的顶点为割点(定义来自啊哈算法),那么问题来了,如何求割点呢???
很容易的一个想法就是我们每次删除一个节点后用bfs或者dfs来遍历图是否依然联通,如果不联通则该点是割点,这种方法的复杂度是O(N(N + M)),太暴力了,我们来想想其他的方法
再介绍其他方法前,我们再来了解几个定义(定义来源于网络)
- DFS搜索树:用DFS对图进行遍历时,按照遍历次序的不同,我们可以得到一棵DFS搜索树,如图(b)所示。
- 树边:(在[2]中称为父子边),在搜索树中的实线所示,可理解为在DFS过程中访问未访问节点时所经过的边。
- 回边:(在[2]中称为返祖边、后向边),在搜索树中的虚线所示,可理解为在DFS过程中遇到已访问节点时所经过的边。
通过观察图(2),我们发现有俩类节点可能成为割点
- 对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点;(说明删除该节点会产生至少俩棵不联通的子树)
- 对非叶子节点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算法求割点割边相关推荐
- Tarjan算法求无向图割边割点、最近公共祖先的总结
无向图tarjan求割边割点.最近公共祖先总结 割点:删除这个点之后整个图变成不连通的两个部分的点 割点集合:在一个无向图中删除该集合中的所有点,能使原图变成互不相连的连通块的点的集合 点连通度 ...
- C++算法篇:DFS超详细解析(2)--- tarjan算法求无向图割边
<<<上一篇 系列文章目录 ①:无向图基本概念 ②:tarjan算法求无向图割边 前言 第一次写算法,讲得肯不透彻,有误还请指教awa 文章目录 系列文章目录 一.回顾 二.tarj ...
- 海亮DAY8 关于Tarjan算法用于割点割边相关感受
Tarjan 简介 Tarjan算法在求割点,割边,连通分量方面及其高效,在军事,交通,设计等方面有重要作用. 由于Tarjan算法思想并不难懂,在此不放上Tarjan算法的具体介绍. [Usaco2 ...
- Tarjan算法求割点与割边(python3实现)
from typing import List, Tuple''' Trajan算法求无向图的桥 '''class Tarjan:# 求无向连通图的桥@staticmethoddef getCutti ...
- tarjan算法求无向图的割点和桥
tarjan算法求无向图的割点与桥 一篇tarjan算法求割点与桥的完整的解释,写的真的好认真 以下代码来自kuangbin的模板 4.5 图的割点.桥和双连通分支的基本概念 [点连通度与边连通度] ...
- 算法提高课-图论-有向图的强连通分量-AcWing 1174. 受欢迎的牛:tarjan算法求强连通分量、tarjan算法板子、强连通图
文章目录 题目解答 题目来源 题目解答 来源:acwing 分析: 强连通图:给定一张有向图.若对于图中任意两个结点x,y,既存在从x到y的路径,也存在从y到x的路径,则称该有向图是"强连通 ...
- tarjan算法求SCC,e-DCC,v-DCC
在介绍算法之前先引入几个概念: 时间戳: 在深度优先搜索时,每个点xxx第一次被访问的顺序为时间戳,记作dfn[x]dfn[x]dfn[x](从111开始) 追溯值: 从xxx开始走,所能遍历到的最小 ...
- Tarjan算法求桥
桥的定义: 在图论中,一条边被称为"桥"代表这条边一旦被删除,这张图的连通块数量会增加.等价地说,一条边是一座桥当且仅当这条边不在任何环上.一张图可以有零或多座桥. 理论: 从某个 ...
- SPF Tarjan算法求无向图割点(关节点)入门题
SPF 题目抽象,给出一个连通图的一些边,求关节点.以及每个关节点分出的连通分量的个数 邻接矩阵只要16ms,而邻接表却要32ms, 花费了大量的时间在加边上. // time 16ms 1 ...
最新文章
- 微信小程序制作-随笔2
- linux内存转换成功,linux系统内存转换成硬盘使用
- mysql数据库限制多次登录失败,限定用户重试时间
- 剑指offer 面试64题
- html 未来元素绑定事件,jquery on如何给未来元素绑定事件?
- mysql自动编号_MySQL自动编号与主键
- stm32f102 SPI口重复初始化引起的问题及解决办法
- eclipse查看git地址_Git大文件管理:函数计算和OSS支持的Git LFS服务器
- ThinkPHP之MVC简析
- 红河学院计算机科学与技术,2016年红河学院计算机科学与技术专业最低分是多少?...
- 人的“肥胖”基因FTO可促进水稻和土豆增产50% - 中国粮食、中国饭碗
- SPOJ DQUERY 求区间内不同数的个数 主席树
- 为什么你还一直在穷打工?
- tiny core linux 7.1,极度简约 最小 Linux 发行版 Tiny Core Linux 7.1 发布
- 5G终端天线设计,到底有多难?
- (七)linux操作系统-linux韩顺平2021笔记
- 流量转发的思路-软件流量转发 管家婆 客户端 端口 更改
- linux终端ANSI转义字符
- CentOS 6.4 安装D-Link 525(RT5360)无线网卡驱动
- a轮b轮c轮天使轮区别是什么?
热门文章
- 全新VR全景可视化制作小程序系统源码+公众号功能模块1.0.28
- Codeforces 600E Lomsat gelral 树上启发式合并,线段树合并.
- 标准日语初级 测试题
- 3D目标检测中点云的表征方式总结(一)
- html网页无法进入系统,怎样解决无法打开网页的问题
- ELK 7.17.5 集群部署及使用
- Unity2017五子棋大战_人机_双人_UNET联网
- Hadoop HA高可用集群搭建(Hadoop+Zookeeper+HBase)
- java工程师安卓方向_校招|vivo提前批JAVA工程师Android方向
- 计算机无线鼠标用不了怎么办,电脑的无线鼠标突然用不了了有什么方法解决