Tarjan 算法求割点、 割边、 强联通分量
Tarjan算法是一个基于dfs的搜索算法, 可以在O(N+M)的复杂度内求出图的割点、割边和强联通分量等信息。
https://www.cnblogs.com/shadowland/p/5872257.html该算法的手动模拟详细
再Tarjan算法中,有如下定义。
DFN[ i ] : 在DFS中该节点的时间戳
LOW[ i ] : 为i能追溯到最早的时间戳
在一个无向图中,如果有一个顶点,删除这个顶点以及这个顶点相关联的边以后,图的连通分量增多,就称这个点为割点。
割点伪代码:
tarjan(u, father){
Index ++; //当前时间戳++
Dfn[cur] = Index; //当前顶点cur的时间戳
Low[cur] = Index; //当前顶点能访问最早的时间戳, 一开始为自己 for each (u, v) in E // 枚举每一条边 if (v is not visited) // 如果节点v未被访问过, 即Dfn[v] = 0
child++; //v是u的孩子, 把u的孩子记录下来
tarjan(v) // 继续向下找 Low[u] = min(Low[u], Low[v]);
if (Low[v] is equal or bigger than Dfn[u], and u is not thr root)
u is cut point. //如果low[v] >= Dfn[u] 而且u不是根节点, 说明v不能通过u访问到u前面的结点, u是割点!
if (u is root and u have two children)
u is cut point. //如果u是根节点, 那么他至少要有2个或以上的孩子才是割点
if(v is visited) //如果v访问过, 那么更新Low[u] 为 Low[u] 和Dfn[v]的较小值
Low[u] = min(Low[u], Dfn[v]);
}
#include <bits/stdc++.h>
using namespace std;
const int maxn = + ;
vector<int> G[maxn];
int n, m, root;
int num[maxn], low[maxn],flag[maxn], Index;
set<int> ans;
void dfs(int cur, int father)
{
int child = ; //当前结点孩子数目
Index ++; //当前时间戳++
num[cur] = Index; //当前顶点cur的时间戳
low[cur] = Index; //当前顶点能访问最早的时间戳, 一开始为自己
for(int i = ; i < G[cur].size(); i++) //枚举所有与顶点cur相连的顶点
{
int v = G[cur][i];
if(num[v] == ) //如果没被访问过
{
child++; //那么该结点v就是cur的孩子
dfs(v, cur); //访问该结点v
low[cur] = min(low[v], low[cur]); //更新cur能到达的最早顶点时间戳
if(low[v] >= num[cur] && cur != root) //如果不是根节点, 而且满足low[v] >= num[cur]
{
flag[cur] = ; //那么cur就是割点
}
if(cur == root && child == ) //如果是根节点, 那么至少有2个孩子才是割点
flag[cur] = ;//这里其实是>=2, 但因为dfs搜到第二个孩子就会把该点认为是割点
}
else if(v != father)//如果 v不是cur的父亲, 而且被访问过, 说明v是cur的祖先,要更新low[cur]
{
low[cur] = min(low[cur], num[v]);
}
}
return;
}
int main()
{ int i, j, u, v;
int kase = ;
while(~scanf("%d %d", &n,&m))
{
Index = ;
memset(num,,sizeof(num));
memset(low,,sizeof(low));
memset(flag,,sizeof(flag));
for(int i = ; i < n; i++) G[i].clear();
ans.clear();
for(int i = ; i < m; i++)
{
scanf("%d %d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
root = ;//标记根节点
dfs(, root);
for(int i = ; i < n; i++)
if(flag[i] == ) printf("%d ", i);
puts("");
}
return ;
} 割点实现代码
割点实现代码
在一个无向图中,如果有一条边,删除这条边以后,图的连通分量增多,就称这个点为割边。
割边伪代码:
tarjan(u, father){
Index ++; //当前时间戳++
Dfn[cur] = Index; //当前顶点cur的时间戳
Low[cur] = Index; //当前顶点能访问最早的时间戳, 一开始为自己 for each (u, v) in E // 枚举每一条边 if (v is not visited) // 如果节点v未被访问过, 即Dfn[v] = 0
tarjan(v) // 继续向下找 Low[u] = min(Low[u], Low[v]);
if (Low[v] is bigger than Dfn[u])
E(u,v) is a cut edge// 割边的条件为, Low[v] > dfn[u]
// 即如果不通过这条边, 去不到他的祖先(包括父亲)的点 if(v is visited) //如果v访问过, 那么更新Low[u] 为 Low[u] 和Dfn[v]的较小值
Low[u] = min(Low[u], Dfn[v]);
}
#include <bits/stdc++.h>
using namespace std;
const int maxn = + ;
vector<int> G[maxn];
int n, m, root;
int num[maxn], low[maxn],flag[maxn], Index;
void dfs(int cur, int father)
{
// printf("cur : %d father : %d\n", cur, father);
//求割边则不需要记录孩子数目
Index ++; //当前时间戳++
num[cur] = Index; //当前顶点cur的时间戳
low[cur] = Index; //当前顶点能访问最早的时间戳, 一开始为自己
for(int i = ; i < G[cur].size(); i++) //枚举所有与顶点cur相连的顶点
{
int v = G[cur][i];
if(num[v] == ) //如果没被访问过
{
dfs(v, cur); //访问该结点v
low[cur] = min(low[v], low[cur]); //更新cur能到达的最早顶点时间戳
if(low[v] > num[cur]) //如果不是根节点, 而且满足low[v] >= num[cur]
{
printf("%d %d\n", cur, v);
} }
else if(v != father)//如果 v不是cur的父亲, 而且被访问过, 说明v是cur的祖先,要更新low[cur]
{
low[cur] = min(low[cur], num[v]);
}
}
return;
} int main()
{
// freopen("1.txt","r", stdin);
int i, j, u, v;
int kase = ;
while(~scanf("%d %d", &n,&m))
{
Index = ;
memset(num,,sizeof(num));
memset(low,,sizeof(low));
memset(flag,,sizeof(flag));
for(int i = ; i < n; i++) G[i].clear();
for(int i = ; i < m; i++)
{
scanf("%d %d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
root = ;//标记根节点
dfs(, root); puts("");
}
return ;
}
割边实现代码
在一个有向图G中,有一个子图,这个子图任意2个点都互相可达,我们就叫这个子图叫做强连通子图。
有向图的极大强连通子图为强连通分量
强连通分量伪代码
tarjan(u){ DFN[u]=Low[u]=++Index // 为节点u设定次序编号和Low初值 Stack.push(u) // 将节点u压入栈中 for each (u, v) in E // 枚举每一条边 if (v is not visted) // 如果节点v未被访问过 tarjan(v) // 继续向下找 Low[u] = min(Low[u], Low[v]) else if (v in S) // 如果节点u还在栈内 Low[u] = min(Low[u], DFN[v]) if (DFN[u] == Low[u]) // 如果节点u是强连通分量的根, 因为该强联通分量中, 该点Low值最小(出现最早)。 repeat v = S.pop
until (u == v) // v是栈顶元素,将v退栈,为该强连通分量中一个顶点, 退栈的所有元素为该强联通分量中的点
// 如果退栈的栈顶元素是u, 说明以v为根的强联通分量已经全部找出。 }
#include <stack>
#include <cstdio>
#include <vector>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = ;
vector<int> G[maxn];
int n , m;
int dfn[maxn], low[maxn], color[maxn], out_degree[maxn];
int dfs_num = , col_num = ;
bool vis[maxn];//标记元素是否在栈中
stack<int> s;
void Tarjan(int u)
{
dfn[ u ] = dfs_num;
low[ u ] = dfs_num++;
vis[u] = true;
s.push(u);
for(int i = ; i < G[u].size(); i++)
{
int v = G[u][i];
if( ! dfn[v]) //如果v没有访问过
{
Tarjan( v ); //访问v, 并更新low[u]
low[u] = min(low[v], low[u]);
}
else if(vis[v]) //如果v在栈中
{
low[u] = min(low[u], dfn[v]); //更新low[u]
}
}
if(dfn[u] == low[u])
{
vis[u] = false;
color[u] = col_num;//把强连通分量记录成统一编号、 类似并查集
int t;
for(;;){
int t = s.top(); s.pop();
color[t] = col_num;
vis[t] = false;
if(t == u) break;
}
col_num++;
}
}
int main()
{
scanf("%d %d", &n,&m);
for(int i = ; i < m; i++)
{
int u , v;
scanf("%d %d", &u, &v);
G[u].push_back(v); } //因为图不一定连通, 所以每个顶点都要访问一次
for(int i = ; i <= n; i++){
if(!dfn[i])
Tarjan(i);
} //输出强连通分量
for(int i = ; i < col_num; i++){
for(int u = ; u <= n; u++){
if(color[u] == i) printf("%d ", u);
}
puts("");
} return ;
}
强连通分量实现
部分实现参考《啊哈算法》
Tarjan 算法求割点、 割边、 强联通分量的更多相关文章
- Tarjan算法求割点
(声明:以下图片来源于网络) Tarjan算法求出割点个数 首先来了解什么是连通图 在图论中,连通图基于连通的概念.在一个无向图 G 中,若从顶点i到顶点j有路径相连(当然从j到i也一定有路径),则称 ...
- tarjan算法(割点/割边/点连通分量/边连通分量/强连通分量)
tarjan算法是在dfs生成一颗dfs树的时候按照访问顺序的先后,为每个结点分配一个时间戳,然后再用low[u]表示结点能访问到的最小时间戳 以上的各种应用都是在此拓展而来的. 割点:如果一个图去掉 ...
- Tarjan算法与割点割边
目录 Tarjan算法与无向图的连通性 1:基础概念 2:Tarjan判断割点 3:Tarjan判断割边 Tarjan算法与无向图的连通性 1:基础概念 在说Tarjan算法求解无向图的连通性之前,先 ...
- tarjan算法求割点cojs 8
tarjan求割点:cojs 8. 备用交换机 ★★ 输入文件:gd.in 输出文件:gd.out 简单对比时间限制:1 s 内存限制:128 MB [问题描述] n个城市之间有通讯网 ...
- 无向连通图求割点(tarjan算法去掉改割点剩下的联通分量数目)
poj2117 Electricity Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 3603 Accepted: 12 ...
- [学习笔记] Tarjan算法求桥和割点
在之前的博客中我们已经介绍了如何用Tarjan算法求有向图中的强连通分量,而今天我们要谈的Tarjan求桥.割点,也是和上篇有博客有类似之处的. 关于桥和割点: 桥:在一个有向图中,如果删去一条边,而 ...
- 强联通分量之kosaraju算法
首先定义:强联通分量是有向图G=(V, E)的最大结点集合,满足该集合中的任意一对结点v和u,路径vu和uv同时存在. kosaraju算法用来寻找强联通分量.对于图G,它首先随便找个结点dfs,求出 ...
- 【强联通分量缩点】【最长路】【spfa】CH Round #59 - OrzCC杯NOIP模拟赛day1 队爷的讲学计划
10分算法:对于城市网络为一条单向链的数据, 20分算法:对于n<=20的数据,暴力搜出所有的可能路径. 结合以上可以得到30分. 60分算法:分析题意可得使者会带着去的城市也就是这个城市所在强 ...
- tarjan模板 强联通分量+割点+割边
// https://www.cnblogs.com/stxy-ferryman/p/7779347.html ; struct EDGE { int to, nt; }e[N*N]; int hea ...
随机推荐
- [洛谷P4185] [USACO18JAN]MooTube
题目链接: 传送门 题意: 给定一颗N个节点的树,定义两点距离为他们之间路径中边权最小值. Q次询问K,V,询问到V距离>=K的点有多少(不含V) 呃呃呃呃考试的时候直奔了T3,结果公式推挂了( ...
- STM32HAL库学习之前言
HAL库:HAL 的全称是: Hardware Abstraction Layer (硬件抽象层) ,是ST最新推荐的库.包括基本库和扩展库(功能外展):三种编程模型(轮询.中断和 DMA) 灵活的回 ...
- mysql 三大范式【转载】
第一范式(1NF,normal format):字段不能再分. 这是字段的原子性.例如:字段“学期时间”:2014-9-1,2015-1-15. 这个字段“学期时间”可以再分为“学期开始时间”,201 ...
- Win7下单机版的伪分布式solrCloud环境搭建Tomcat+solr+zookeeper【转】
Win7下单机版的伪分布式solrCloud环境搭建Tomcat+solr+zookeeper 1.软件工具箱 在本文的实践中,需要用到以下的软件: Tomcat-7.0.62+solr-5.0.0+ ...
- JVM 内存机制理解【转自http://www.cnblogs.com/dingyingsi/p/3760447.html】
我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区,用户磁盘和CPU的交互,而CPU运转速度越来越快,磁盘远远跟不上CPU的读写速度,才设计了内存,用户缓冲用户IO等待导致CPU的等 ...
- Spark MLlib编程API入门系列之特征选择之R模型公式(RFormula)
不多说,直接上干货! 特征选择里,常见的有:VectorSlicer(向量选择) RFormula(R模型公式) ChiSqSelector(卡方特征选择). RFormula用于将数据中的字段通过R ...
- wamp无法进入phpMyAdmin或localhost的解决方法
我用的是最新版的wampsever5,在win7(64位)下安装正常使用,没有无法进入phpMyAdmin的问题,但是我在虚拟机安装了win8(64位专业版),测试在win8下面的使用情况时,就有问题 ...
- css3 blur模糊解决ie6-ie9兼容
css3 blur模糊是css3的新特性,但是不兼容ie6-ie9,以下代码可以解决此问题: filter: progid:DXImageTransform.Microsoft.Blur(Pixel ...
- Android手机屏幕投射到电脑神器Vysor
做android开发的,经常要把手机屏幕投射到电脑,用来演示.普遍的解决方案是360或者豌豆荚的演示功能,缺点是延迟非常厉害,大概有3秒左右,非常影响演示效果.以下介绍Vysor,几乎0延迟,能与手机 ...
- pandas中loc-iloc-ix的使用
转自:https://www.jianshu.com/p/d6a9845a0a34 Pandas中loc,iloc,ix的使用 使用 iloc 从DataFrame中筛选数据 iloc 是基于“位置” ...