文字描述
相关定义:假若在删去顶点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的祖先的回边。(这段话可能不太好理解,可以结合示意图和代码来看)。
示意图
算法分析
算法的时间复杂度和深度优先遍历一样。
代码实现
1 // 2 // Created by lady on 18-12-15. 3 // 4 5 6 #include <stdlib.h> 7 #include <stdio.h> 8 9 #define MAX_VERTEX_NUM 20 //最大顶点数 10 11 typedef enum {DG, DN, UDG, UDN} GraphKind; //{有向图,有向网,无向图,无向网} 12 typedef char VertexType;//顶点类型 13 typedef struct ArcNode{ 14 int adjvex; 15 struct ArcNode *nextarc; 16 char info[5]; 17 }ArcNode; 18 typedef struct VNode{ 19 VertexType data; 20 ArcNode *firstarc; 21 }VNode, AdjList[MAX_VERTEX_NUM]; 22 typedef struct{ 23 AdjList vertices; 24 int vexnum; 25 int arcnum; 26 int kind; 27 }ALGraph; 28 29 //若G中存在顶点u,则返回该顶点在图中位置;否则返回-1。 30 static int LocateVex(ALGraph G, VertexType v) 31 { 32 int i = 0; 33 for(i=0; i<G.vexnum; i++){ 34 if(G.vertices[i].data == v) 35 return i; 36 } 37 return -1; 38 } 39 static VertexType LocateData(ALGraph G, int index) 40 { 41 return G.vertices[index].data; 42 } 43 44 //在链表L的头部前插入v 45 static int InsFirst(ArcNode *L, int v) 46 { 47 ArcNode *n = (ArcNode *)malloc(sizeof(struct ArcNode)); 48 n->adjvex = v; 49 n->nextarc = L->nextarc; 50 L->nextarc = n; 51 return 0; 52 } 53 54 //采用邻接表的存储结构,构造无向图 55 static int CreateUDG(ALGraph *G) 56 { 57 int i = 0, j = 0, k = 0; 58 int v1 = 0, v2 = 0; 59 char tmp[10] = {0}; 60 printf("输入顶点数,弧数:"); 61 scanf("%d,%d", &G->vexnum, &G->arcnum); 62 for(i=0; i<G->vexnum; i++){ 63 printf("输入第%d个顶点: ", i+1); 64 memset(tmp, 0, sizeof(tmp)); 65 scanf("%s", tmp); 66 G->vertices[i].data = tmp[0]; 67 G->vertices[i].firstarc = malloc(sizeof(struct ArcNode)); 68 G->vertices[i].firstarc->adjvex = -1; 69 G->vertices[i].firstarc->nextarc = NULL; 70 } 71 for(k=0; k<G->arcnum; k++){ 72 printf("输入第%d条弧(顶点1, 顶点2): ", k+1); 73 memset(tmp, 0, sizeof(tmp)); 74 scanf("%s", tmp); 75 sscanf(tmp, "%c,%c", &v1, &v2); 76 i = LocateVex(*G, v1); 77 j = LocateVex(*G, v2); 78 InsFirst(G->vertices[i].firstarc, j); 79 InsFirst(G->vertices[j].firstarc, i); 80 } 81 return 0; 82 } 83 84 static int CreateGraph(ALGraph *G) 85 { 86 switch(G->kind){ 87 case DG: 88 case DN: 89 case UDN: 90 return -1; 91 case UDG: 92 return CreateUDG(G); 93 default: 94 return -1; 95 } 96 } 97 98 //输出图的信息 99 static void printG(ALGraph G) 100 { 101 if(G.kind == DG){ 102 printf("类型:有向图;顶点数 %d, 弧数 %d\n", G.vexnum, G.arcnum); 103 }else if(G.kind == DN){ 104 printf("类型:有向网;顶点数 %d, 弧数 %d\n", G.vexnum, G.arcnum); 105 }else if(G.kind == UDG){ 106 printf("类型:无向图;顶点数 %d, 弧数 %d\n", G.vexnum, G.arcnum); 107 }else if(G.kind == UDN){ 108 printf("类型:无向网;顶点数 %d, 弧数 %d\n", G.vexnum, G.arcnum); 109 } 110 int i = 0; 111 ArcNode *p = NULL; 112 for(i=0; i<G.vexnum; i++){ 113 printf("%c\t", G.vertices[i].data); 114 p = G.vertices[i].firstarc; 115 while(p){ 116 printf("%d\t", p->adjvex); 117 p = p->nextarc; 118 } 119 printf("\n"); 120 } 121 return; 122 } 123 124 static int count = 0; 125 static int visited[MAX_VERTEX_NUM] = {0}; 126 static int low[MAX_VERTEX_NUM] = {0}; 127 128 //从第v0个顶点出发深度优先遍历图G,查找并输出关节点。 129 void DFSArticul(ALGraph G, int v0) 130 { 131 int w = 0; 132 int min = 0; 133 ArcNode *p = NULL; 134 135 count += 1; 136 min = count; 137 visited[v0] = count; 138 printf("visited[%d,%c]=%d\n", v0, G.vertices[v0].data, count); 139 for(p=G.vertices[v0].firstarc->nextarc; p; p=p->nextarc){ 140 w = p->adjvex; 141 if(visited[w] !=0 ){ 142 printf("回边: (%d,%c), (%d,%c)\n", v0, G.vertices[v0].data, w, G.vertices[w].data); 143 } 144 if(visited[w] == 0){ 145 DFSArticul(G, w); 146 if(low[w] < min) 147 min = low[w]; 148 if(low[w] >= visited[v0]) 149 printf("关节点 (index %d, data %c) !!!!!\n", v0, G.vertices[v0].data); 150 }else if(visited[w] < min){ 151 min = visited[w]; 152 } 153 } 154 low[v0] = min; 155 printf("low[%d,%c]=%d\n", v0, G.vertices[v0].data, min); 156 } 157 158 void FindArticul(ALGraph G) 159 { 160 count = 1; 161 visited[0] = 1; 162 low[0] = 1; 163 int i = 0; 164 int v = 0; 165 ArcNode *p = NULL; 166 for(i=1; i<G.vexnum; ++i){ 167 visited[i] = 0; 168 } 169 p = G.vertices[0].firstarc->nextarc; 170 v = p->adjvex; 171 printf("visit[0,%c]=1 low[0,%c]=1\n", G.vertices[0].data, G.vertices[0].data); 172 printf("从第(%d,%c)个顶点出发深度优先遍历图G,查找并输出关节点.\n", v, G.vertices[v].data); 173 DFSArticul(G, v); 174 if(count < G.vexnum){ 175 //生成树的根至少有两颗子树 176 printf("生成树的根至少有两颗子树 因为count %d < %d\n", count, G.vexnum); 177 printf("关节点 (index %d, data %c) !!!!!\n", 0, G.vertices[0].data); 178 while(p->nextarc){ 179 p = p->nextarc; 180 v = p->adjvex; 181 if(visited[v] == 0){ 182 printf("从第(%d,%c)个顶点出发深度优先遍历图G,查找并输出关节点.\n", v, G.vertices[v].data); 183 DFSArticul(G, v); 184 } 185 } 186 } 187 printf("index:\t\t"); 188 for(i=0;i<G.vexnum; i++){ 189 printf("%d\t", i); 190 } 191 printf("\n"); 192 193 printf("data:\t\t"); 194 for(i=0;i<G.vexnum; i++){ 195 printf("%c\t", G.vertices[i].data); 196 } 197 printf("\n"); 198 199 printf("visited[]:\t"); 200 for(i=0;i<G.vexnum; i++){ 201 printf("%d\t", visited[i]); 202 } 203 printf("\n"); 204 205 printf("low[]:\t\t"); 206 for(i=0;i<G.vexnum; i++){ 207 printf("%d\t", low[i]); 208 } 209 printf("\n"); 210 } 211 212 int main(int argc, char *argv[]) 213 { 214 printf("创建一个无向图, "); 215 ALGraph G; 216 G.kind = UDG; 217 CreateGraph(&G); 218 219 printf("\n打印此无向图中存放的结点信息, "); 220 printG(G); 221 222 printf("\n查找并输出以邻接表作存储结构的图G的全部关节点:\n"); 223 FindArticul(G); 224 return 0; 225 }