图->连通性->关节点和重连通分量
文字描述
相关定义:假若在删去顶点v以及和v相关联的各边之后,将图的一个连通分量分割成两个或两个以上的连通分量,则称顶点v为该图的一个关节点.一个没有关节点的连通图称为重连通图. 在重连通图上,任意一对顶点之间至少存在两条路径, 则在删去某个顶点以及依附于该顶点的各边时也不破坏图的连通性.若在连通图上至少删除k个顶点才能破坏图的连通性,则称此图的连通度为k.
判断图是否是重连通的,可以先利用深度优先搜索求得图的关节点,一个没有关节点的图便是重连通的.由深度优先生成树可得出两类关节点的特性:
1 若生成树的根有两颗或两颗以上的子树, 则此根顶点必为关节点. 因为.若删去根顶点,生成树便变成生成森林.如示意图中的顶点A
2 若生成树中某个非叶子顶点v,其某棵子树的根和子树中的其他结点均没有指向v的祖先的回边,则v为关节点. 因为,若删去v,则其子树和图的其他部分被分割开来.如示意图中的顶点B,D,G
若该图Graph={V,{Edge}}重新定义遍历时的访问数组visit,并引入一个新的数足low,则由一次深度优先遍历便可求得连通图中存在的所有关节点。

若对于某个顶点v,存在函数节点w,且low[w]>=visited[v], 则v必为关节点。因为当w是v的孩子结点时,low[w]>=visited[v], 表明w及其子孙均无指向v的祖先的回边。(这段话可能不太好理解,可以结合示意图和代码来看)。
示意图




算法分析
算法的时间复杂度和深度优先遍历一样。
代码实现
//
// Created by lady on 18-12-15.
// #include <stdlib.h>
#include <stdio.h> #define MAX_VERTEX_NUM 20 //最大顶点数 typedef enum {DG, DN, UDG, UDN} GraphKind; //{有向图,有向网,无向图,无向网}
typedef char VertexType;//顶点类型
typedef struct ArcNode{
int adjvex;
struct ArcNode *nextarc;
char info[];
}ArcNode;
typedef struct VNode{
VertexType data;
ArcNode *firstarc;
}VNode, AdjList[MAX_VERTEX_NUM];
typedef struct{
AdjList vertices;
int vexnum;
int arcnum;
int kind;
}ALGraph; //若G中存在顶点u,则返回该顶点在图中位置;否则返回-1。
static int LocateVex(ALGraph G, VertexType v)
{
int i = ;
for(i=; i<G.vexnum; i++){
if(G.vertices[i].data == v)
return i;
}
return -;
}
static VertexType LocateData(ALGraph G, int index)
{
return G.vertices[index].data;
} //在链表L的头部前插入v
static int InsFirst(ArcNode *L, int v)
{
ArcNode *n = (ArcNode *)malloc(sizeof(struct ArcNode));
n->adjvex = v;
n->nextarc = L->nextarc;
L->nextarc = n;
return ;
} //采用邻接表的存储结构,构造无向图
static int CreateUDG(ALGraph *G)
{
int i = , j = , k = ;
int v1 = , v2 = ;
char tmp[] = {};
printf("输入顶点数,弧数:");
scanf("%d,%d", &G->vexnum, &G->arcnum);
for(i=; i<G->vexnum; i++){
printf("输入第%d个顶点: ", i+);
memset(tmp, , sizeof(tmp));
scanf("%s", tmp);
G->vertices[i].data = tmp[];
G->vertices[i].firstarc = malloc(sizeof(struct ArcNode));
G->vertices[i].firstarc->adjvex = -;
G->vertices[i].firstarc->nextarc = NULL;
}
for(k=; k<G->arcnum; k++){
printf("输入第%d条弧(顶点1, 顶点2): ", k+);
memset(tmp, , sizeof(tmp));
scanf("%s", tmp);
sscanf(tmp, "%c,%c", &v1, &v2);
i = LocateVex(*G, v1);
j = LocateVex(*G, v2);
InsFirst(G->vertices[i].firstarc, j);
InsFirst(G->vertices[j].firstarc, i);
}
return ;
} static int CreateGraph(ALGraph *G)
{
switch(G->kind){
case DG:
case DN:
case UDN:
return -;
case UDG:
return CreateUDG(G);
default:
return -;
}
} //输出图的信息
static void printG(ALGraph G)
{
if(G.kind == DG){
printf("类型:有向图;顶点数 %d, 弧数 %d\n", G.vexnum, G.arcnum);
}else if(G.kind == DN){
printf("类型:有向网;顶点数 %d, 弧数 %d\n", G.vexnum, G.arcnum);
}else if(G.kind == UDG){
printf("类型:无向图;顶点数 %d, 弧数 %d\n", G.vexnum, G.arcnum);
}else if(G.kind == UDN){
printf("类型:无向网;顶点数 %d, 弧数 %d\n", G.vexnum, G.arcnum);
}
int i = ;
ArcNode *p = NULL;
for(i=; i<G.vexnum; i++){
printf("%c\t", G.vertices[i].data);
p = G.vertices[i].firstarc;
while(p){
printf("%d\t", p->adjvex);
p = p->nextarc;
}
printf("\n");
}
return;
} static int count = ;
static int visited[MAX_VERTEX_NUM] = {};
static int low[MAX_VERTEX_NUM] = {}; //从第v0个顶点出发深度优先遍历图G,查找并输出关节点。
void DFSArticul(ALGraph G, int v0)
{
int w = ;
int min = ;
ArcNode *p = NULL; count += ;
min = count;
visited[v0] = count;
printf("visited[%d,%c]=%d\n", v0, G.vertices[v0].data, count);
for(p=G.vertices[v0].firstarc->nextarc; p; p=p->nextarc){
w = p->adjvex;
if(visited[w] != ){
printf("回边: (%d,%c), (%d,%c)\n", v0, G.vertices[v0].data, w, G.vertices[w].data);
}
if(visited[w] == ){
DFSArticul(G, w);
if(low[w] < min)
min = low[w];
if(low[w] >= visited[v0])
printf("关节点 (index %d, data %c) !!!!!\n", v0, G.vertices[v0].data);
}else if(visited[w] < min){
min = visited[w];
}
}
low[v0] = min;
printf("low[%d,%c]=%d\n", v0, G.vertices[v0].data, min);
} void FindArticul(ALGraph G)
{
count = ;
visited[] = ;
low[] = ;
int i = ;
int v = ;
ArcNode *p = NULL;
for(i=; i<G.vexnum; ++i){
visited[i] = ;
}
p = G.vertices[].firstarc->nextarc;
v = p->adjvex;
printf("visit[0,%c]=1 low[0,%c]=1\n", G.vertices[].data, G.vertices[].data);
printf("从第(%d,%c)个顶点出发深度优先遍历图G,查找并输出关节点.\n", v, G.vertices[v].data);
DFSArticul(G, v);
if(count < G.vexnum){
//生成树的根至少有两颗子树
printf("生成树的根至少有两颗子树 因为count %d < %d\n", count, G.vexnum);
printf("关节点 (index %d, data %c) !!!!!\n", , G.vertices[].data);
while(p->nextarc){
p = p->nextarc;
v = p->adjvex;
if(visited[v] == ){
printf("从第(%d,%c)个顶点出发深度优先遍历图G,查找并输出关节点.\n", v, G.vertices[v].data);
DFSArticul(G, v);
}
}
}
printf("index:\t\t");
for(i=;i<G.vexnum; i++){
printf("%d\t", i);
}
printf("\n"); printf("data:\t\t");
for(i=;i<G.vexnum; i++){
printf("%c\t", G.vertices[i].data);
}
printf("\n"); printf("visited[]:\t");
for(i=;i<G.vexnum; i++){
printf("%d\t", visited[i]);
}
printf("\n"); printf("low[]:\t\t");
for(i=;i<G.vexnum; i++){
printf("%d\t", low[i]);
}
printf("\n");
} int main(int argc, char *argv[])
{
printf("创建一个无向图, ");
ALGraph G;
G.kind = UDG;
CreateGraph(&G); printf("\n打印此无向图中存放的结点信息, ");
printG(G); printf("\n查找并输出以邻接表作存储结构的图G的全部关节点:\n");
FindArticul(G);
return ;
}
利用深度优先遍历输出图中的关节点
代码运行
/home/lady/CLionProjects/untitled/cmake-build-debug/untitled
创建一个无向图, 输入顶点数,弧数:13,16
输入第1个顶点: A
输入第2个顶点: B
输入第3个顶点: C
输入第4个顶点: D
输入第5个顶点: E
输入第6个顶点: F
输入第7个顶点: G
输入第8个顶点: H
输入第9个顶点: I
输入第10个顶点: J
输入第11个顶点: K
输入第12个顶点: L
输入第13个顶点: M
输入第1条弧(顶点1, 顶点2): A,B
输入第2条弧(顶点1, 顶点2): A,C
输入第3条弧(顶点1, 顶点2): A,F
输入第4条弧(顶点1, 顶点2): A,L
输入第5条弧(顶点1, 顶点2): L,J
输入第6条弧(顶点1, 顶点2): M,J
输入第7条弧(顶点1, 顶点2): M,L
输入第8条弧(顶点1, 顶点2): M,B
输入第9条弧(顶点1, 顶点2): B,D
输入第10条弧(顶点1, 顶点2): D,E
输入第11条弧(顶点1, 顶点2): B,G
输入第12条弧(顶点1, 顶点2): B,H
输入第13条弧(顶点1, 顶点2): G,H
输入第14条弧(顶点1, 顶点2): G,I
输入第15条弧(顶点1, 顶点2): G,K
输入第16条弧(顶点1, 顶点2): H,K 打印此无向图中存放的结点信息, 类型:无向图;顶点数 13, 弧数 16
A -1 11 5 2 1
B -1 7 6 3 12 0
C -1 0
D -1 4 1
E -1 3
F -1 0
G -1 10 8 7 1
H -1 10 6 1
I -1 6
J -1 12 11
K -1 7 6
L -1 12 9 0
M -1 1 11 9 查找并输出以邻接表作存储结构的图G的全部关节点:
visit[0,A]=1 low[0,A]=1
从第(11,L)个顶点出发深度优先遍历图G,查找并输出关节点.
visited[11,L]=2
visited[12,M]=3
visited[1,B]=4
visited[7,H]=5
visited[10,K]=6
回边: (10,K), (7,H)
visited[6,G]=7
回边: (6,G), (10,K)
visited[8,I]=8
回边: (8,I), (6,G)
low[8,I]=7
关节点 (index 6, data G) !!!!!
回边: (6,G), (7,H)
回边: (6,G), (1,B)
low[6,G]=4
low[10,K]=4
回边: (7,H), (6,G)
回边: (7,H), (1,B)
low[7,H]=4
关节点 (index 1, data B) !!!!!
回边: (1,B), (6,G)
visited[3,D]=9
visited[4,E]=10
回边: (4,E), (3,D)
low[4,E]=9
关节点 (index 3, data D) !!!!!
回边: (3,D), (1,B)
low[3,D]=4
关节点 (index 1, data B) !!!!!
回边: (1,B), (12,M)
回边: (1,B), (0,A)
low[1,B]=1
回边: (12,M), (11,L)
visited[9,J]=11
回边: (9,J), (12,M)
回边: (9,J), (11,L)
low[9,J]=2
low[12,M]=1
回边: (11,L), (9,J)
回边: (11,L), (0,A)
low[11,L]=1
生成树的根至少有两颗子树 因为count 11 < 13
关节点 (index 0, data A) !!!!!
从第(5,F)个顶点出发深度优先遍历图G,查找并输出关节点.
visited[5,F]=12
回边: (5,F), (0,A)
low[5,F]=1
从第(2,C)个顶点出发深度优先遍历图G,查找并输出关节点.
visited[2,C]=13
回边: (2,C), (0,A)
low[2,C]=1
index: 0 1 2 3 4 5 6 7 8 9 10 11 12
data: A B C D E F G H I J K L M
visited[]: 1 4 13 9 10 12 7 5 8 11 6 2 3
low[]: 1 1 1 4 9 1 4 4 7 2 4 1 1 Process finished with exit code 0
图->连通性->关节点和重连通分量的更多相关文章
- 图连通性【tarjan点双连通分量、边双联通分量】【无向图】
根据 李煜东大牛:图连通性若干拓展问题探讨 ppt学习. 有割点不一定有割边,有割边不一定有割点. 理解low[u]的定义很重要. 1.无向图求割点.点双联通分量: 如果对一条边(x,y),如果low ...
- LOJ121 「离线可过」动态图连通性
思路 动态图连通性的板子,可惜我不会在线算法 离线可以使用线段树分治,每个边按照存在的时间插入线段树的对应节点中,最后再dfs一下求出解即可,注意并查集按秩合并可以支持撤销操作 由于大量使用STL跑的 ...
- 【LOJ121】「离线可过」动态图连通性
[LOJ121]「离线可过」动态图连通性 题面 LOJ 题解 线段树分治的经典应用 可以发现每个边出现的时间是一个区间 而我们每个询问是一个点 所以我们将所有边的区间打到一颗线段树上面去 询问每个叶子 ...
- 【BZOJ4025】二分图(LCT动态维护图连通性)
点此看题面 大致题意: 给你一张图以及每条边的出现时间和消失时间,让你求每个时间段这张图是否是二分图. 二分图性质 二分图有一个比较简单的性质,即二分图中不存在奇环. 于是题目就变成了:让你求每个时间 ...
- [LOJ#121]动态图连通性
[LOJ#121]动态图连通性 试题描述 这是一道模板题. 你要维护一张无向简单图.你被要求加入删除一条边及查询两个点是否连通. 0:加入一条边.保证它不存在. 1:删除一条边.保证它存在. 2:查询 ...
- BZOJ1050 [HAOI2006]旅行comf[并查集判图连通性]
★ Description 给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000).给你两个顶点S和T,求 一条路径,使得路径 ...
- 图->连通性->无向图的连通分量和生成树
文字描述 对无向图进行遍历时,对于连通图,仅需从图中任一顶点出发,进行深度优先搜索或广度优先搜索,便可访问到图中所有顶点.但对非连通图,则需从多个顶点出发搜索,每一次从一个新的起始点出发进行搜索过程得 ...
- hdu 4444 Walk (离散化+建图+bfs+三维判重 好题)
Walk Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Submi ...
- 图->连通性->最小生成树(克鲁斯卡尔算法)
文字描述 上一篇博客介绍了最小生成树(普里姆算法),知道了普里姆算法求最小生成树的时间复杂度为n^2, 就是说复杂度与顶点数无关,而与弧的数量没有关系: 而用克鲁斯卡尔(Kruskal)算法求最小生成 ...
随机推荐
- Linux 下的 sleep
最近在阅读 libev 的源码,看到 libev 的代码里面的 sleep 实现, 我觉得可以把这个 sleep 实现单独拿出来,作为参考,以后可以直接拿来用. 代码如下(稍有修改): void ev ...
- 利用cwRsync客户端将Windows下文件同步到Linux
这里不描述Linux服务端安装配置rsync服务的过程,有需要可以在网络上查找相关教程. 1.安装cwRsync客户端下载地址:http://itefix.no/cwrsync/下载文件cwRsync ...
- Java基础(二)面向对象(上)
面向对象基础知识 面向对象是相对面向过程而言的 面向对象和面向过程都是一种思想 面向过程强调的是功能行为 面向对象将功能封装进对象,强调具备了功能的对象 面向对象是基于面向过程的 面向对象的特征: 封 ...
- 线程封装组件(BackgroundWorker)和线程(Thread)
BackgroundWorker是微软的在.net Framwork中添加的一个组件,主要对线程的访问提供了一种安全的方式.简单的说就是对Thread的一次封装. BackgroundWorker位于 ...
- idea 配置 maven 项目
maven 项目 用模块引入进来 1.引入 pom.xml 2.如果不是web则要添加web支持 3.配置资源 类 和依赖 and 项目语言环境 5.配置 artifacts 部署 w ...
- go: writing stat cache:, permission denied
sudo chown -R $(whoami):admin /Users/zhushuyan/go/pkg && sudo chmod -R g+rwx /Users/zhushuya ...
- React+Webpack+Webstorm开发环境搭建
需要安装的软件 node.js npm包管理 Webstorm 由于6.3.0版本之后会自带npm的包管理所以不需要单独的安装npm nodejs(包含npm)安装在默认路径C:\Program Fi ...
- 循环神经网络(Recurrent Neural Networks, RNN)介绍
目录 1 什么是RNNs 2 RNNs能干什么 2.1 语言模型与文本生成Language Modeling and Generating Text 2.2 机器翻译Machine Translati ...
- phpstudy 500 Internal Server Error 解决办法
版本:phpstudy 2018 报错:500 Internal Server Error 原因:手动选择路径的时候,产生了斜杠不同 (正确:"D:/phpStudy/PHPTutoria ...
- 3D Object Classification With Point Convolution —— 点云卷积网络
今天刚刚得到消息,之前投给IROS 2017的文章收录了.很久很久没有写过博客,今天正好借这个机会来谈谈点云卷积网络的一些细节. 1.点云与三维表达 三维数据后者说空间数据有很多种表达方式,比如:RG ...