知识点

假若在删去顶点v以及和v相关联的各边之后,将图的一个连通分量分割成两个或两个以上的连通分量,则称顶点v为该图的一个关节点(articulation point)。一个没有关节点的连通图称为是重连通图(biconnectedgraph)。在重连通图上,任意一对顶点之间至少存在两条路径则在删去某个顶点以及依附于该顶点的各边时也不破坏图的连通性。若在连通图上至少删去k个顶点才能破坏图的连通性,则称此图的连通度为k。关节点和重连通在实际中有较多应用。

显然,一个表示通信网络的图的连通度越高,其系统越可靠,无论是哪一站点出现故障或遭到外界破坏,都不影响系统的正常工作;又如,一个航空网若是重连通的,则当某条航线因天气等某种原因关闭时,旅客仍可从别的航线绕道而行;再如,若将大规模集成电路的关键线路设计成重连通的话,则在某些元件失效的情况下,整个片子的功能不受影响,反之,在战争中,若要摧毁敌方的运输线,仅需破坏其运输网中的关节点即可。

算法实现

//算法7.10
void FindArticul(ALGraph G)
{
    //连通图G以邻接表作存储结构,查找并输出G上全部关节点。算法7.10 
  // 全局量count对访问计数。
	int i, v;
	ArcNode *p;
    int count;
	count = 1;
	low[0] = visited[0] = 1; //设定邻接表上0号顶点为生成树的根 
	for (i = 1; i < G.vexnum; ++i)
		visited[i] = 0; //其余顶点尚未访问 
	p = G.vertices[0].firstarc;
	v = p->adjvex;
	DFSArticul(G, v); // 从第v顶点出发深度优先查找关节点 
	if (count < G.vexnum) // 生成树的根有至少两棵子树
	{
   
		printf("%d %s\n", 0, G.vertices[0].data); // 根是关节点,输出 
		while (p->nextarc)
		{
   
			p = p->nextarc;
			v = p->adjvex;
			if (visited[v] == 0)
				DFSArticul(G, v);
		}
	}
}
//算法7.11
void DFSArticul(ALGraph G, int v0)
{
    /* 从第v0个顶点出发深度优先遍历图G,查找并输出关节点。算法7.11 */
	int min, w;
    int count;
	ArcNode *p;
	visited[v0] = min = ++count; /* v0是第count个访问的顶点 */
	for (p = G.vertices[v0].firstarc; p; p = p->nextarc) /* 对v0的每个邻接顶点检查 */
	{
   
		w = p->adjvex; /* w为v0的邻接顶点 */
		if (visited[w] == 0) /* w未曾访问,是v0的孩子 */
		{
   
			DFSArticul(G, w); /* 返回前求得low[w] */
			if (low[w] < min)
				min = low[w];
			if (low[w] >= visited[v0])
				printf("%d %s\n", v0, G.vertices[v0].data); /* 关节点 */
		}
		else if (visited[w] < min)
			min = visited[w]; /* w已访问,w是v0在生成树上的祖先 */
	}
	low[v0] = min;
}
 

完整代码

#include<malloc.h> /* malloc()等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<process.h> /* exit() */
#include<limits.h> //常量INT_MAX和INT_MIN分别表示最大、最小整数##
#include<string.h> 
/* 函数结果状态代码 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2 
#define MAX_NAME 2 /* 顶点字符串的最大长度+1 */
typedef int InfoType;
typedef char VertexType[MAX_NAME]; /* 字符串类型 */
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
 
 
/* --------------------------------- 图的邻接表存储表示 --------------------------------*/
 
#define MAX_VERTEX_NUM 20
typedef enum {
    DG, DN, AG, AN }GraphKind; /* {有向图,有向网,无向图,无向网} */
typedef struct ArcNode
{
   
	int adjvex; /* 该弧所指向的顶点的位置 */
	struct ArcNode *nextarc; /* 指向下一条弧的指针 */
	InfoType *info; /* 网的权值指针) */
}ArcNode; /* 表结点 */
typedef struct
{
   
	VertexType data; /* 顶点信息 */
	ArcNode *firstarc; /* 第一个表结点的地址,指向第一条依附该顶点的弧的指针 */
}VNode, AdjList[MAX_VERTEX_NUM]; /* 头结点 */
typedef struct
{
   
	AdjList vertices;
	int vexnum, arcnum; /* 图的当前顶点数和弧数 */
	int kind; /* 图的种类标志 */
}ALGraph;
 
/* ------------------------------------------------------------*/

/* ----------- 需要用的图的邻接表存储的基本操作 ----------------*/
 
int LocateVex(ALGraph G, VertexType u)
{
    /* 初始条件: 图G存在,u和G中顶点有相同特征 */
  /* 操作结果: 若G中存在顶点u,则返回该顶点在图中位置;否则返回-1 */
	int i;
	for (i = 0; i < G.vexnum; ++i)
		if (strcmp(u, G.vertices[i].data) == 0)
			return i;
	return -1;
}
 
Status CreateGraph(ALGraph *G)
{
    /* 采用邻接表存储结构,构造没有相关信息的图G(用一个函数构造4种图) */
	int i, j, k;
	int w; /* 权值 */
	VertexType va, vb;
	ArcNode *p;
	printf("请输入图的类型(有向图:0,有向网:1,无向图:2,无向网:3): ");
	scanf("%d", &(*G).kind);
	printf("请输入图的顶点数,边数: ");
	scanf("%d,%d", &(*G).vexnum, &(*G).arcnum);
	printf("请输入%d个顶点的值(<%d个字符):\n", (*G).vexnum, MAX_NAME);
	for (i = 0; i < (*G).vexnum; ++i) /* 构造顶点向量 */
	{
   
		scanf("%s", (*G).vertices[i].data);
		(*G).vertices[i].firstarc = NULL;
	}
	if ((*G).kind == 1 || (*G).kind == 3) /* 网 */
		printf("请顺序输入每条弧(边)的权值、弧尾和弧头(以空格作为间隔):\n");
	else /* 图 */
		printf("请顺序输入每条弧(边)的弧尾和弧头(以空格作为间隔):\n");
	for (k = 0; k < (*G).arcnum; ++k) /* 构造表结点链表 */
	{
   
		if ((*G).kind == 1 || (*G).kind == 3) /* 网 */
			scanf("%d%s%s", &w, va, vb);
		else /* 图 */
			scanf("%s%s", va, vb);
		i = LocateVex(*G, va); /* 弧尾 */
		j = LocateVex(*G, vb); /* 弧头 */
		p = (ArcNode*)malloc(sizeof(ArcNode));
		p->adjvex = j;
		if ((*G).kind == 1 || (*G).kind == 3) /* 网 */
		{
   
			p->info = (int *)malloc(sizeof(int));
			*(p->info) = w;
		}
		else
			p->info = NULL; /* 图 */
		p->nextarc = (*G).vertices[i].firstarc; /* 插在表头 */
		(*G).vertices[i].firstarc = p;
		if ((*G).kind >= 2) /* 无向图或网,产生第二个表结点 */
		{
   
			p = (ArcNode*)malloc(sizeof(ArcNode));
			p->adjvex = i;
			if ((*G).kind == 3) /* 无向网 */
			{
   
				p->info = (int*)malloc(sizeof(int));
				*(p->info) = w;
			}
			else
				p->info = NULL; /* 无向图 */
			p->nextarc = (*G).vertices[j].firstarc; /* 插在表头 */
			(*G).vertices[j].firstarc = p;
		}
	}
	return OK;
}
 
 
Boolean visited[MAX_VERTEX_NUM]; /* 访问标志数组(全局量) */
void(*VisitFunc)(char* v); /* 函数变量(全局量) */
 
 
 
/* ------------------------------------------------------------*/
 
 
 
/* 实现算法7.10、7.11的程序 */
 
int count; /* 全局量count对访问计数 */
int low[MAX_VERTEX_NUM];
 
void DFSArticul(ALGraph G, int v0){
   
 /* 从第v0个顶点出发深度优先遍历图G,查找并输出关节点。算法7.11 */
	int min, w;
	ArcNode *p;
	visited[v0] = min = ++count; /* v0是第count个访问的顶点 */
	for (p = G.vertices[v0].firstarc; p; p = p->nextarc) /* 对v0的每个邻接顶点检查 */
	{
   
		w = p->adjvex; /* w为v0的邻接顶点 */
		if (visited[w] == 0) /* w未曾访问,是v0的孩子 */
		{
   
			DFSArticul(G, w); /* 返回前求得low[w] */
			if (low[w] < min)
				min = low[w];
			if (low[w] >= visited[v0])
				printf("%d %s\n", v0, G.vertices[v0].data); /* 关节点 */
		}
		else if (visited[w] < min)
			min = visited[w]; /* w已访问,w是v0在生成树上的祖先 */
	}
	low[v0] = min;
}
 
void FindArticul(ALGraph G)
{
    /* 连通图G以邻接表作存储结构,查找并输出G上全部关节点。算法7.10 */
  /* 全局量count对访问计数。 */
	int i, v;
	ArcNode *p;
	count = 1;
	low[0] = visited[0] = 1; /* 设定邻接表上0号顶点为生成树的根 */
	for (i = 1; i < G.vexnum; ++i)
		visited[i] = 0; /* 其余顶点尚未访问 */
	p = G.vertices[0].firstarc;
	v = p->adjvex;
	DFSArticul(G, v); /* 从第v顶点出发深度优先查找关节点 */
	if (count < G.vexnum) /* 生成树的根有至少两棵子树 */
	{
   
		printf("%d %s\n", 0, G.vertices[0].data); /* 根是关节点,输出 */
		while (p->nextarc)
		{
   
			p = p->nextarc;
			v = p->adjvex;
			if (visited[v] == 0)
				DFSArticul(G, v);
		}
	}
}

int main(){
   
	int i;
	ALGraph g;
	printf("请选择无向图\n");
	CreateGraph(&g);
	printf("输出关节点:\n");
	FindArticul(g);
	printf(" i G.vertices[i].data visited[i] low[i]\n");
	for (i = 0; i < g.vexnum; ++i)
		printf("%2d %9s %14d %8d\n", i, g.vertices[i].data, visited[i], low[i]);
}

测试样例

请选择无向图
请输入图的类型(有向图:0,有向网:1,无向图:2,无向网:3): 2
请输入图的顶点数,边数: 13,17
请输入13个顶点的值(<2个字符):
A B C D E F G H I J K L M
请顺序输入每条弧()的弧尾和弧头(以空格作为间隔):
A B
A C
A F
A L
B C
B D
B G
B H
B M
D E
G H
G I
G K
H K
J L
J M
L M

运行结果

输出关节点:
6 G
1 B
3 D
1 B
0 A
 i G.vertices[i].data visited[i] low[i]
 0         A              1        1
 1         B              5        1
 2         C             12        1
 3         D             10        5
 4         E             11       10
 5         F             13        1
 6         G              8        5
 7         H              6        5
 8         I              9        8
 9         J              4        2
10         K              7        5
11         L              2        1
12         M              3        1

更多数据结构代码实现请关注我的专栏数据结构

或者进入2021-10-16【严蔚敏数据结构代码实现合集】【c语言学习必备】学习