RobertTarjan真的是一个传说级的大人物。

他发明的LCT,SplayTree这些数据结构真的给我带来了诸多便利,各种动态图论题都可以用LCT解决。

而且,Tarjan并不只发明了LCT,他对计算机科学做出的贡献真的很多。

这一篇我就来以他名字命名的Tarjan算法可以O(n)求出无向图的割点和桥。

进一步可以求出无向图的DCC( 双连通分量 )。不止无向图,Tarjan算法还可以求出有向图的SCC( 强连通分量 )。

Tarjan算法基于dfs,接下来我们引入几个基本概念。

dfn:时间戳

我们对一张图进行深度优先遍历,根据第一次访问到它的时间顺序给它打上一个标记,这个标记就是时间戳。

搜索树:

在一张无向连通图中选定任意一个节点进行深度优先遍历,每个点仅访问一次。所有发生了递归的边会构成一棵树,我们称其为无向连通图的“搜索树”。

追溯值:

除了时间戳,Tarjan算法还引入了另一个概念:“追溯值” low。

我们用subtree(x)表示搜索树中以x为根的子树,low[x]定义为下列节点的时间戳的最小值:

1. subtree(x)中的节点 2. 通过一条不在搜索树上的边,能够到达subtree(x)中的节点

我们来画一个图理解一下:(方便起见,图中的节点编号就是它的时间戳)

图中红色的边就是这张图的搜索树

那么我们容易得出:subtree(2)={4,3},5可以通过不在搜索树上的边到达subtree(2)。

所以,low[2]=min{dfn[4],dfn[3],dfn[5]},得出low[2]=3。

根据定义来计算low[x]的方法就非常明显了。因为subtree(x)包括x,所以先令low[x]=dfn[x]。

然后遍历从x出发的每一条边(x,y),计算low[x]。

接下来给出无向图的桥和割点判定法则。

无向边(x,y)是桥,当且仅当x在搜索树上的一个子节点y满足low[y]>dfn[x]。

若x不是搜索树的根节点,则x是割点当且仅当搜索树上的一个子节点y满足low[y]>=dfn[x]。

若x是根节点,则x是割点当且仅当搜索树上存在至少两个x的子节点y1,y2满足上式。

桥边有以下性质:

1. 桥一定是搜索树中的边 2. 一个简单环中边都不是桥边

一个环被称为简单环当且仅当其包含的所有点都只在这个环中被经过了一次。

扩展内容:这里给出用dfs求出一个图中所有简单环的代码

int cnt;
void dfs(int u){
dfn[u]=++cnt;
for(int i=head[u];i;i=nxt(i)){
int v=to(i);
if(v==fa[u])continue;
if(!dfn[v])fa[v]=u,dfs(v);
else if(dfn[u]<dfn[v]){
printf("%d",v);
do{
printf(" %d",fa[v]);v=fa[v];
}while(v!=u);
//找完一个环了
putchar('\n');
}
}
}

这个作为扩展内容就不再展开叙述。

下面给出求出无向图中所有的桥的代码:

#include<bits/stdc++.h>
#define N 100010
using namespace std;
inline int read(){
int data=,w=;char ch=;
while(ch!='-' && (ch<''||ch>''))ch=getchar();
if(ch=='-')w=-,ch=getchar();
while(ch>='' && ch<='')data=data*+ch-'',ch=getchar();
return data*w;
}
struct Edge{
int nxt,to;
#define nxt(x) e[x].nxt
#define to(x) e[x].to
}e[N<<];
int dfn[N],low[N],tot=;//储存边的编号,由于要用^1找反向边,从1开始
int bridge[N],head[N];
int n,m,cnt;
void addedge(int f,int t){
nxt(++tot)=head[f];to(tot)=t;head[f]=tot;
}
void tarjan(int x,int in_edge){//in_edge表示递归进入每个节点的边的编号
dfn[x]=low[x]=++cnt;
for(int i=head[x];i;i=nxt(i)){
int y=to(i);
if(!dfn[y]){
tarjan(y,i);
low[x]=min(low[x],low[y]);//在搜索树上的边
if(low[y]>dfn[x])//桥判定法则
bridge[i]=bridge[i^]=;//这条边和它的反向边都是桥
}else if(i!=(in_edge^))
low[x]=min(low[x],dfn[y]);//不在搜索树上的边
}
}
int main(){
n=read();m=read();
for(int i=;i<=m;i++){
int x=read(),y=read();
addedge(x,y);addedge(y,x);
}
for(int i=;i<=n;i++)
if(!dfn[i])tarjan(i,);
for(int i=;i<tot;i+=)
if(bridge[i])
printf("%d %d\n",to(i^),to(i));//输出桥两边的点
}

以上就是求无向图中所有桥的程序了,可以自己画图模拟一下tarjan算法的流程加深理解。

下面给出求无向图中所有割点的程序:

这里需要注意的是,由于割点判定法则是小于等于号,所以不需要考虑父节点和重边的问题,所有dfn[x]都可以用来更新low[x]

#include<bits/stdc++.h>
#define N 100010
using namespace std;
inline int read(){
int data=,w=;char ch=;
while(ch!='-' && (ch<''||ch>''))ch=getchar();
if(ch=='-')w=-,ch=getchar();
while(ch>='' && ch<='')data=data*+ch-'',ch=getchar();
return data*w;
}
struct Edge{
int nxt,to;
#define nxt(x) e[x].nxt
#define to(x) e[x].to
}e[N<<];
int head[N],dfn[N],low[N],rt,tot=,n,m,cnt;
int cut[N];
inline void addedge(int f,int t){
nxt(++tot)=head[f];to(tot)=t;head[f]=tot;
}
void tarjan(int x){
dfn[x]=low[x]=++cnt;
int flag=;
for(int i=head[x];i;i=nxt(i)){
int y=to(i);
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){//就一个割点判断法则没必要解释什么了吧?
flag++;
if(x!=rt||flag>)cut[x]=;
}
}else low[x]=min(low[x],dfn[y]);
}
}
int main(){
n=read();m=read();
for(int i=;i<=m;i++){
int x=read(),y=read();
if(x==y)continue; //自环直接判掉好吧,不多bb
addedge(x,y);addedge(y,x);
}
for(int i=;i<=n;i++)
if(!dfn[i])rt=i,tarjan(i);//无向图不一定连通,对每一个连通块都要跑一发tarjan
for(int i=;i<=n;i++)
if(cut[i])printf("%d ",i);
return ;
}

桥边判定法则和割点判定法则后面会update上,这一篇暂时更到这里,下一篇讲e-DCC和v-DCC的求法

主要是联赛在即,更新尽量多的算法扎实自己基础才要紧些...请多见谅

[Tarjan系列] Tarjan算法求无向图的桥和割点的更多相关文章

  1. tarjan算法求无向图的桥、边双连通分量并缩点

    // tarjan算法求无向图的桥.边双连通分量并缩点 #include<iostream> #include<cstdio> #include<cstring> ...

  2. Light OJ - 1026 - Critical Links(图论-Tarjan算法求无向图的桥数) - 带详细注释

     原题链接   无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 也可以先用Tajan()进行dfs算出所有点 的low和dfn值,并记录dfs过程中每个 点的父节点:然后再把所有点遍历一遍 ...

  3. Hdu 4738【tanjan求无向图的桥】割边判定定理 dfn[x] < low[y]

    题目: 曹操在长江上建立了一些点,点之间有一些边连着.如果这些点构成的无向图变成了连通图,那么曹操就无敌了.刘备为了防止曹操变得无敌,就打算去摧毁连接曹操的点的桥.但是诸葛亮把所有炸弹都带走了,只留下 ...

  4. [Tarjan系列] Tarjan算法求无向图的双连通分量

    这篇介绍如何用Tarjan算法求Double Connected Component,即双连通分量. 双联通分量包括点双连通分量v-DCC和边连通分量e-DCC. 若一张无向连通图不存在割点,则称它为 ...

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

    一.基本概念 1.桥:是存在于无向图中的这样的一条边,如果去掉这一条边,那么整张无向图会分为两部分,这样的一条边称为桥无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 2.割点:无向连通图中 ...

  6. hdu 4738 Caocao's Bridges 求无向图的桥【Tarjan】

    <题目链接> 题目大意: 曹操在长江上建立了一些点,点之间有一些边连着.如果这些点构成的无向图变成了连通图,那么曹操就无敌了.周瑜为了防止曹操变得无敌,就打算去摧毁连接曹操的点的桥.但是诸 ...

  7. [Tarjan系列] Tarjan算法与有向图的SCC

    前面的文章介绍了如何用Tarjan算法计算无向图中的e-DCC和v-DCC以及如何缩点. 本篇文章资料参考:李煜东<算法竞赛进阶指南> 这一篇我们讲如何用Tarjan算法求有向图的SCC( ...

  8. 蓝桥杯历届试题 危险系数(dfs或者并查集求无向图关于两点的割点个数)

    Description 抗日战争时期,冀中平原的地道战曾发挥重要作用. 地道的多个站点间有通道连接,形成了庞大的网络.但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系. 我们来定义一个 ...

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

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

随机推荐

  1. 最全的tcpdump使用详解

    简介 用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者的定义对网络上的数据包进行截获的包分析工具. tcpdump可以将网络中传送的数据包的 ...

  2. Thymeleaf th:include、th:replace引用

    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" ...

  3. 进程 | 线程 | 当Linux多线程遭遇Linux多进程

    背景 本文并不是介绍Linux多进程多线程编程的科普文,如果希望系统学习Linux编程,可以看[<Unix环境高级编程>第3版] 本文是描述多进程多线程编程中遇到过的一个坑,并从内核角度分 ...

  4. mongodb 添加用户报错TypeError:db.addUser is not a function (mongodb3.4.1)

    1:问题如下: 原因是 新版的MongoDB已经不支持addUser方法了. 改成createUser了. 使用方法如下    2:具体解释一下db.createUser()方法的用法   定义: 创 ...

  5. 014-操作系统下验证下载文件的 MD5/SHA1/SHA256

    一.mac 1.md5 openssl md5 /path/to/file 新的macOS默认支持:md5 filename 2.sha256 openssl dgst -sha256 /path/t ...

  6. [Scikit-learn] 1.5 Generalized Linear Models - SGD for Regression

    梯度下降 一.亲手实现“梯度下降” 以下内容其实就是<手动实现简单的梯度下降>. 神经网络的实践笔记,主要包括: Logistic分类函数 反向传播相关内容 Link: http://pe ...

  7. 搭建无人值守安装服务器(CentOS)

    使用PXE+DHCP+TFTP+Kickstart+FTP搭建无人值守安装服务器.一般只有频繁安装系统才会搭建无人值守安装服务器. 虚拟机环境:youxi1,CentOS7系统双网卡,一个网卡桥接模式 ...

  8. Python将多个excel表格合并为一个表格

    Python将多个excel表格合并为一个表格 生活中经常会碰到多个excel表格汇总成一个表格的情况,比如你发放了一份表格让班级所有同学填写,而你负责将大家的结果合并成一个.诸如此类的问题有很多.除 ...

  9. 【leetcode_easy】557. Reverse Words in a String III

    problem 557. Reverse Words in a String III solution1:字符流处理类istringstream. class Solution { public: s ...

  10. 事理学神器PDCA

    做事情都按PDCA循环来做,基本就是一个靠谱的人. 这个方法论其实也符合架构师的思维中的分治理论.把大事拆分成一件件小事,并把小事做好. Plan Do Check Action