图的联通
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
enum _falg{NOTVIS=0,VIS,OVER}flag[N]; // NOTVIS、VIS、OVER分别表示顶点没有被访问过、顶点被访问过但未删除、顶点已 被删除的状态。 int color[N]; //color[i],表示i在哪个强连通分量 stack<int> st; //堆栈,辅助作用 int low[N],index[N];//low,与其相邻但未删除定点的最小访问时间
INDEX[6]=LOW[6],找到了一个强连通分量。退栈到u=v为止,{6}为一个强连 通分量。
强连通分量(Tarjan算法)
返回节点5,发现INDEX[5]=LOW[5],退栈后{5}为一个强连通分量。
强连通分量(Tarjan算法)
返回节点3,继续搜索到节点4,把4加入堆栈。发现节点4向节点1有后向边,
① 初始化index[x]和low[x]; ② 对于x所有的邻接顶点v:
如果没有访问过,则用同样方法访问v,同时维护low[x];
如果访问过,但没有删除,就维护low[x]。 ③ 如果index[x]=low[x],那么输出相应的强连通分量
强连通分量(Tarjan算法)
从节点1开始DFS,把遍历到的节点加入栈中。搜索到节点u=6时,
缩图,能够方便地进行其它操作,而且时间效率会大大地提高,原先
对多个点的操作可以简化为对它们所属的缩点的操作。 求强连通分量常常用于求拓扑排序之前,因为原图往往有环,无法进 行拓扑排序,而求强连通分量后所建立的缩图则是有向无环图,方便 进行拓扑排序。
判断图中两点之间是否连通
1、Floyed算法 时间复杂度:O(N3 ) 算法实现: Floyd算法的基本思想如下:从任意节点A到任意节点B的是否 连通不外乎2种可能,1是直接从A到B,2是从A经过若干个节 点到B,所以,我们假设 dist(AB)为节点A到节点B的是否连通 ,对于每一个节点K,我们检查dist(AK) && dist(KB) 是否成 立,如果成立,证明从A到K再到B的存在路径,也就是AB联 通。我们便设置 dist(AB) = dis(AB)||dist(AK)&&dist(KB),
节点1还在栈中,所以LOW[4]=1。节点6已经出栈,(4,6)是横叉边,返回3, (3,4)为树枝边,所以LOW[3]=LOW[4]=1。
强连通分量(Tarjan算法)
继续回到节点1,最后访问节点2。访问边(2,4),4还在栈中,所以
LOW[2]=LOW[4]=1。返回1后,发现INDEX[1]=LOW[1],把栈中节点全部 取出,组成一个强连通分量 {1,3,4,2}。
图中连线代表连通。如果我们在最内层检查所有节点K,那么对于1->4,
我们无法发现路径,结果就是不连通,而这显然是不正确的,真实的路径
是1->2->3->4,结果是连通。造成错误的原因就是我们把检查所有节点 K放在最内层,造成过早的把1到4是否连通确定下来了,当确定1->4是否
连通时,dis(1,3)尚未被计算。
强连通分量
在一个非强连通图中极大的强连通子图就是该图的强连通分量。比如 图三中子图{1,2,3,5}是一个强连通分量,子图{4}是一个强连通分量。
图的连通
由一个强连通分量内的所有点所组成的集合称为缩点。 在有向图中的所有缩点和所有缩点之间的边所组成的集合称为
该有向图的缩图。
把有向图中具有相同性质的点找出来,形成一个集合(缩点),建立
这样一来,当我们遍历完所有节点K,dist(AB)中记录的便是A
到B的是否连通。
判断图中两点之间是否连通
把相连的两点间的距离设为dis[i][ j]=true,不相连的两点设为 dis[i][ j]=false,用Floyed算法的变形:
for (k = 1; k <= n; k++)
for (i = 1; i <= n; i++) for ( j = 1; j <= n; j++)
dis[i][ j] = dis[i][ j] || (dis[i][k] && dis[k][ j]);
最后如果dis[i][ j]=true的话,那么就说明两点之间有路径连通 。 有向图与无向图都适用。
判断图中两点之间是否连通
这里我们要注意循环的嵌套顺序,如果把检查所有节点K放在最内层, 那么结果将是不正确的,为什么呢?因为这样便过早的把i到j是否连通 确定下来了,而当后面存在连通的路径时,已经不再会更新了。
至此,算法结束。经过该算法,求出了图中全部的三个强连通分量 {1,3,4,2},{5},{6}。 可以发现,运行Tarjan算法的过程中,每个顶点都被访问了一次,且只 进出了一次堆栈,每条边也只被访问了一次,所以该算法的时间复杂度 为O(N+M)。
强连通分量(Tarjan算法)
const int N=100,M=1000;
强连通分量(Kosaraju算法)
int main(){ freopen("test.in","r",stdin); memset(g,0x7f,sizeof(g)); cin>>n>>m; int x,y; for(int i=1;i<=m;i++){ cin>>x>>y; cin>>g[x][y]; } kosaraju(); return 0; }
判断图中两点之间是否连通
2、遍历算法(dfs)
时间复杂度:O(N2 )
算法实现: 从任意一个顶点出发,进行一次遍历,能够从这个点出发到达的点就 与起点是联通的。这样就可以求出此顶点和其它各个顶点的连通情况 。所以只要把每个顶点作为出发点都进行一次遍历,就能知道任意两 个顶点之间是否有路存在。 有向图与无向图都适用。
强连通分量(Kosaraju算法)
void kosaraju(){ memset(color,0,sizeof(color)); for(int i=1;i<=n;i++) //dfs进行,拓扑排序 if(!color[i])dfs(i); memset(color,0,sizeof(color)); /* 我们本需对原图的边反向,但由于我们使用邻接矩阵储 存图,所以反向的图的邻接矩阵 即ຫໍສະໝຸດ Baidu图邻接矩阵的对角线对称矩阵,所以我们什么都不用做 ,只需访问对称矩阵即可*/ num=0; for(int i=n;i>=1;i--) //按照拓扑排序进行dfs if(!color[i]){ num++; ndfs(i); } cout<<num<<endl; }
强连通分量(Tarjan算法)
任何一个强连通分量,必定是对原图的深度优先搜索树的子树(记住这句话)。 那么,我们只要确定每个强连通分量的子树的根,然后根据这些根从树的最低 层开始,一个一个的拿出强连通分量。 我们维护两个数组,一个是index,一个是low。其中index[i]表示顶点i的开始 访问时间。low[i]是最小访问时间,初始化为index[i],维护时low[i]取它与 low[j]的最小值,其中j是与顶点i邻接但未删除的顶点。
强连通分量(Kosaraju算法)
反图的意思就是把所有的边反过来,即g[i][ j] = g[ j][i]
原图
反图
强连通分量(Kosaraju算法)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=1000; const int INF=0x7f7f7f7f; int g[N][N],n,m,color[N],dfn[N],cnt=0,num; void dfs(int k){ color[k]=1; for(int i=1;i<=n;i++) if(g[k][i]<INF&&!color[i]) dfs(i); dfn[++cnt]=k; //记录第cnt个出栈的为k } void ndfs(int k){ color[k]=num; //本次染色的点,都属于num,用color数组记录 for(int i=1;i<=n;i++) if(g[i][k]<INF&&!color[i]) //注意我们访问的是原矩阵的对称矩阵 dfs(i); }
缩图,能够方便地进行其它操作,而且时间效率会大大地提高,原先
对多个点的操作可以简化为对它们所属的缩点的操作。 求强连通分量常常用于求拓扑排序之前,因为原图往往有环,无法进 行拓扑排序,而求强连通分量后所建立的缩图则是有向无环图,方便 进行拓扑排序。
强连通分量(Kosaraju算法)
该算法可用来计算有向图的强连通分量个数,并收缩强连通分量。 这个算法可以说是最容易理解,最通用的算法,其比较关键的部分是同时 应用了原图G和反图G’()。操作步骤如下: ① 对原图进行DFS并将出栈顺序进行逆序,得到的顺序就是拓扑顺序; ② 将原图每条边进行反向; ③ 按照①中生成顺序再进行DFS染色,染成同色的即一个强连通块。 该算法具有一个隐藏性质:如果我们把求出来的每个强连通分量收缩成一 个点,并且用求出每个强连通分量的顺序来标记收缩后的结点,那么这个 顺序其实就是强连通分量收缩成点后形成的有向无环图的拓扑序列。
3、并查集
时间复杂度:O(mlgn) 算法实现:
依次遍历所有的边进行合并,如果两个点的父节点相同,说明连通。
适用于:无向图
最小环问题
最小环问题 最小环就是指在一张图中找出一个环,使得这个环上的各条边的 权值之和最小。在Floyed的同时,可以顺便算出最小环。 记两点间的最短路为dis[i][j],g[i][j]为边<i,j>的权值。 for (k = 1; k <= n; k++) { for (i = 1; i <= k-1; i++) for (j = 1; j <= k-1; j++) answer = min(answer,dis[i][j]+g[j][k]+g[k][i]); for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) dis[i][j]:=min(dis[i][j],dis[i][k]+dis[k][j]); } answer即为这张图的最小环。 一个环中的最大结点为k(编号最大),与它相连的两个点为i,j,这 个环的最短长度为g[i][k]+g[k][j]+(i到j的路径中,所有结点编号都小于k 的最短路径长度)。 根据Floyed的原理,在最外层循环做了k-1次之后,dis[i][j]则代 表了i到j的路径中,所有结点编号都小于k的最短路径。
的强连通分量一定是以该根为根结点(剩下结点)的子树。在深度优先遍历的 时候维护一个堆栈,每次访问一个新结点,就压入堆栈。 因为当前结点是这个强连通分量中最先被压入堆栈的,那么在当前结点以后压 入堆栈的并且仍在堆栈中的结点都属于这个强连通分量。
强连通分量(Tarjan算法)
算法实现——对于所有未访问的结点x,都进行以下操作:
图的联通
图的连通
连通
如果图中结点U,V之间存在一条从U通过若干条边、点到达V的通路 ,则称U、V 是连通的。
连通图
如果图G中任意两点都连通时,称G为连通图。
连通分量
无向图中的极大连通子图称为连通分量。
图的连通
强连通图
在一个强连通图中,任意两个点都通过一定路径互相连通。比如图一 是一个强连通图,而图二不是。因为没有一条路使得点4到达点1、2 或3。
在一次深搜的回溯过程中,如果发现low[i]=index[i],那么,当前顶点就是
一个强连通分量的根(因为如果它不是强连通分量的根,那么它一定是属于另 一个强连通分量,而且它的根是当前顶点的祖宗,那么存在包含当前顶点的到 其祖宗的回路,可知low[i]一定被更改为一个比index[i]更小的值)。
拿出强连通分量的方法很简单。如果当前结点为一个强连通分量的根,那么它
综上所述,该算法一定能找到图中最小环。
联通分量
统计无向图的强连通分量个数:使用并查集,最后只需统 计父亲结点个数
强连通分量
由一个强连通分量内的所有点所组成的集合称为缩点。 在有向图中的所有缩点和所有缩点之间的边所组成的集合称为
该有向图的缩图。
把有向图中具有相同性质的点找出来,形成一个集合(缩点),建立
INDEX[6]=LOW[6],找到了一个强连通分量。退栈到u=v为止,{6}为一个强连 通分量。
强连通分量(Tarjan算法)
返回节点5,发现INDEX[5]=LOW[5],退栈后{5}为一个强连通分量。
强连通分量(Tarjan算法)
返回节点3,继续搜索到节点4,把4加入堆栈。发现节点4向节点1有后向边,
① 初始化index[x]和low[x]; ② 对于x所有的邻接顶点v:
如果没有访问过,则用同样方法访问v,同时维护low[x];
如果访问过,但没有删除,就维护low[x]。 ③ 如果index[x]=low[x],那么输出相应的强连通分量
强连通分量(Tarjan算法)
从节点1开始DFS,把遍历到的节点加入栈中。搜索到节点u=6时,
缩图,能够方便地进行其它操作,而且时间效率会大大地提高,原先
对多个点的操作可以简化为对它们所属的缩点的操作。 求强连通分量常常用于求拓扑排序之前,因为原图往往有环,无法进 行拓扑排序,而求强连通分量后所建立的缩图则是有向无环图,方便 进行拓扑排序。
判断图中两点之间是否连通
1、Floyed算法 时间复杂度:O(N3 ) 算法实现: Floyd算法的基本思想如下:从任意节点A到任意节点B的是否 连通不外乎2种可能,1是直接从A到B,2是从A经过若干个节 点到B,所以,我们假设 dist(AB)为节点A到节点B的是否连通 ,对于每一个节点K,我们检查dist(AK) && dist(KB) 是否成 立,如果成立,证明从A到K再到B的存在路径,也就是AB联 通。我们便设置 dist(AB) = dis(AB)||dist(AK)&&dist(KB),
节点1还在栈中,所以LOW[4]=1。节点6已经出栈,(4,6)是横叉边,返回3, (3,4)为树枝边,所以LOW[3]=LOW[4]=1。
强连通分量(Tarjan算法)
继续回到节点1,最后访问节点2。访问边(2,4),4还在栈中,所以
LOW[2]=LOW[4]=1。返回1后,发现INDEX[1]=LOW[1],把栈中节点全部 取出,组成一个强连通分量 {1,3,4,2}。
图中连线代表连通。如果我们在最内层检查所有节点K,那么对于1->4,
我们无法发现路径,结果就是不连通,而这显然是不正确的,真实的路径
是1->2->3->4,结果是连通。造成错误的原因就是我们把检查所有节点 K放在最内层,造成过早的把1到4是否连通确定下来了,当确定1->4是否
连通时,dis(1,3)尚未被计算。
强连通分量
在一个非强连通图中极大的强连通子图就是该图的强连通分量。比如 图三中子图{1,2,3,5}是一个强连通分量,子图{4}是一个强连通分量。
图的连通
由一个强连通分量内的所有点所组成的集合称为缩点。 在有向图中的所有缩点和所有缩点之间的边所组成的集合称为
该有向图的缩图。
把有向图中具有相同性质的点找出来,形成一个集合(缩点),建立
这样一来,当我们遍历完所有节点K,dist(AB)中记录的便是A
到B的是否连通。
判断图中两点之间是否连通
把相连的两点间的距离设为dis[i][ j]=true,不相连的两点设为 dis[i][ j]=false,用Floyed算法的变形:
for (k = 1; k <= n; k++)
for (i = 1; i <= n; i++) for ( j = 1; j <= n; j++)
dis[i][ j] = dis[i][ j] || (dis[i][k] && dis[k][ j]);
最后如果dis[i][ j]=true的话,那么就说明两点之间有路径连通 。 有向图与无向图都适用。
判断图中两点之间是否连通
这里我们要注意循环的嵌套顺序,如果把检查所有节点K放在最内层, 那么结果将是不正确的,为什么呢?因为这样便过早的把i到j是否连通 确定下来了,而当后面存在连通的路径时,已经不再会更新了。
至此,算法结束。经过该算法,求出了图中全部的三个强连通分量 {1,3,4,2},{5},{6}。 可以发现,运行Tarjan算法的过程中,每个顶点都被访问了一次,且只 进出了一次堆栈,每条边也只被访问了一次,所以该算法的时间复杂度 为O(N+M)。
强连通分量(Tarjan算法)
const int N=100,M=1000;
强连通分量(Kosaraju算法)
int main(){ freopen("test.in","r",stdin); memset(g,0x7f,sizeof(g)); cin>>n>>m; int x,y; for(int i=1;i<=m;i++){ cin>>x>>y; cin>>g[x][y]; } kosaraju(); return 0; }
判断图中两点之间是否连通
2、遍历算法(dfs)
时间复杂度:O(N2 )
算法实现: 从任意一个顶点出发,进行一次遍历,能够从这个点出发到达的点就 与起点是联通的。这样就可以求出此顶点和其它各个顶点的连通情况 。所以只要把每个顶点作为出发点都进行一次遍历,就能知道任意两 个顶点之间是否有路存在。 有向图与无向图都适用。
强连通分量(Kosaraju算法)
void kosaraju(){ memset(color,0,sizeof(color)); for(int i=1;i<=n;i++) //dfs进行,拓扑排序 if(!color[i])dfs(i); memset(color,0,sizeof(color)); /* 我们本需对原图的边反向,但由于我们使用邻接矩阵储 存图,所以反向的图的邻接矩阵 即ຫໍສະໝຸດ Baidu图邻接矩阵的对角线对称矩阵,所以我们什么都不用做 ,只需访问对称矩阵即可*/ num=0; for(int i=n;i>=1;i--) //按照拓扑排序进行dfs if(!color[i]){ num++; ndfs(i); } cout<<num<<endl; }
强连通分量(Tarjan算法)
任何一个强连通分量,必定是对原图的深度优先搜索树的子树(记住这句话)。 那么,我们只要确定每个强连通分量的子树的根,然后根据这些根从树的最低 层开始,一个一个的拿出强连通分量。 我们维护两个数组,一个是index,一个是low。其中index[i]表示顶点i的开始 访问时间。low[i]是最小访问时间,初始化为index[i],维护时low[i]取它与 low[j]的最小值,其中j是与顶点i邻接但未删除的顶点。
强连通分量(Kosaraju算法)
反图的意思就是把所有的边反过来,即g[i][ j] = g[ j][i]
原图
反图
强连通分量(Kosaraju算法)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=1000; const int INF=0x7f7f7f7f; int g[N][N],n,m,color[N],dfn[N],cnt=0,num; void dfs(int k){ color[k]=1; for(int i=1;i<=n;i++) if(g[k][i]<INF&&!color[i]) dfs(i); dfn[++cnt]=k; //记录第cnt个出栈的为k } void ndfs(int k){ color[k]=num; //本次染色的点,都属于num,用color数组记录 for(int i=1;i<=n;i++) if(g[i][k]<INF&&!color[i]) //注意我们访问的是原矩阵的对称矩阵 dfs(i); }
缩图,能够方便地进行其它操作,而且时间效率会大大地提高,原先
对多个点的操作可以简化为对它们所属的缩点的操作。 求强连通分量常常用于求拓扑排序之前,因为原图往往有环,无法进 行拓扑排序,而求强连通分量后所建立的缩图则是有向无环图,方便 进行拓扑排序。
强连通分量(Kosaraju算法)
该算法可用来计算有向图的强连通分量个数,并收缩强连通分量。 这个算法可以说是最容易理解,最通用的算法,其比较关键的部分是同时 应用了原图G和反图G’()。操作步骤如下: ① 对原图进行DFS并将出栈顺序进行逆序,得到的顺序就是拓扑顺序; ② 将原图每条边进行反向; ③ 按照①中生成顺序再进行DFS染色,染成同色的即一个强连通块。 该算法具有一个隐藏性质:如果我们把求出来的每个强连通分量收缩成一 个点,并且用求出每个强连通分量的顺序来标记收缩后的结点,那么这个 顺序其实就是强连通分量收缩成点后形成的有向无环图的拓扑序列。
3、并查集
时间复杂度:O(mlgn) 算法实现:
依次遍历所有的边进行合并,如果两个点的父节点相同,说明连通。
适用于:无向图
最小环问题
最小环问题 最小环就是指在一张图中找出一个环,使得这个环上的各条边的 权值之和最小。在Floyed的同时,可以顺便算出最小环。 记两点间的最短路为dis[i][j],g[i][j]为边<i,j>的权值。 for (k = 1; k <= n; k++) { for (i = 1; i <= k-1; i++) for (j = 1; j <= k-1; j++) answer = min(answer,dis[i][j]+g[j][k]+g[k][i]); for (i = 1; i <= n; i++) for (j = 1; j <= n; j++) dis[i][j]:=min(dis[i][j],dis[i][k]+dis[k][j]); } answer即为这张图的最小环。 一个环中的最大结点为k(编号最大),与它相连的两个点为i,j,这 个环的最短长度为g[i][k]+g[k][j]+(i到j的路径中,所有结点编号都小于k 的最短路径长度)。 根据Floyed的原理,在最外层循环做了k-1次之后,dis[i][j]则代 表了i到j的路径中,所有结点编号都小于k的最短路径。
的强连通分量一定是以该根为根结点(剩下结点)的子树。在深度优先遍历的 时候维护一个堆栈,每次访问一个新结点,就压入堆栈。 因为当前结点是这个强连通分量中最先被压入堆栈的,那么在当前结点以后压 入堆栈的并且仍在堆栈中的结点都属于这个强连通分量。
强连通分量(Tarjan算法)
算法实现——对于所有未访问的结点x,都进行以下操作:
图的联通
图的连通
连通
如果图中结点U,V之间存在一条从U通过若干条边、点到达V的通路 ,则称U、V 是连通的。
连通图
如果图G中任意两点都连通时,称G为连通图。
连通分量
无向图中的极大连通子图称为连通分量。
图的连通
强连通图
在一个强连通图中,任意两个点都通过一定路径互相连通。比如图一 是一个强连通图,而图二不是。因为没有一条路使得点4到达点1、2 或3。
在一次深搜的回溯过程中,如果发现low[i]=index[i],那么,当前顶点就是
一个强连通分量的根(因为如果它不是强连通分量的根,那么它一定是属于另 一个强连通分量,而且它的根是当前顶点的祖宗,那么存在包含当前顶点的到 其祖宗的回路,可知low[i]一定被更改为一个比index[i]更小的值)。
拿出强连通分量的方法很简单。如果当前结点为一个强连通分量的根,那么它
综上所述,该算法一定能找到图中最小环。
联通分量
统计无向图的强连通分量个数:使用并查集,最后只需统 计父亲结点个数
强连通分量
由一个强连通分量内的所有点所组成的集合称为缩点。 在有向图中的所有缩点和所有缩点之间的边所组成的集合称为
该有向图的缩图。
把有向图中具有相同性质的点找出来,形成一个集合(缩点),建立