图->连通性->关节点和重连通分量
文字描述
相关定义:假若在删去顶点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)算法求最小生成 ...
随机推荐
- MATLAB 统计不同区间中元素的个数
使用 find 命令: x = :;%生成数组 k = find( x > & x < );%查找大于2小于5的元素的数组下标 size(k,) %统计的元素的个数
- PHP微信开发之模板消息回复
参考:http://www.jb51.net/article/87269.htm 代码: <?php //$ac = file_get_contents('https://api.weixin. ...
- 自然语言处理中的N-Gram模型
N-Gram(有时也称为N元模型)是自然语言处理中一个非常重要的概念,通常在NLP中,人们基于一定的语料库,可以利用N-Gram来预计或者评估一个句子是否合理.另外一方面,N-Gram的另外一个作用是 ...
- java 定时任务之一 @Scheduled注解(第一种方法)
使用spring @Scheduled注解执行定时任务: 运行!!! 关于Cron表达式(转载) 表达式网站生成: http://cron.qqe2.com/ 直接点击 作者:http://blog ...
- CentOS 6.5 x64下查找依赖包,或用YUM安装
查看某个命令YUM上的安装源 1)当某个命令不存时进行查询所依赖的包,如:pstree [root@localhost ~]# yum provides pstree 已加载插件:fastestmir ...
- 强化学习-时序差分算法(TD)和SARAS法
1. 前言 我们前面介绍了第一个Model Free的模型蒙特卡洛算法.蒙特卡罗法在估计价值时使用了完整序列的长期回报.而且蒙特卡洛法有较大的方差,模型不是很稳定.本节我们介绍时序差分法,时序差分法不 ...
- pip 安装出现超时问题的解决
pip 安装出现超时问题的解决 我们在用默认的pip源进行安装python库时,会出现超时问题下载不了,如下图显示所示: 那么我们应该如何解决呢? 方法: 在自己电脑的 C:\Users\yanji ...
- Criteo电面二
是第二次Video电面.本来约的是个俄罗斯人,结果面试时才发现换了一位国人大哥.面试这么久,还是第一次遇到国人,然后就被放水了,真给力! 第二天通知约onsite,查了地图,公司就在斯坦福对面.希望能 ...
- Stack堆栈的数据结构
1.
- Oracle数据库自带表空间
需求:需要整理现场用户创建的表空间以及其存储数据,进行规范化管理.在整理用户现场建立的表空间时,需要排除掉非用户创建的表空间,所有首先需要那些表空间是用户创建的,那些是Oracle自带的. 本机测试建 ...