最简单也是最直接的算法是,删除一个点然后判断连通性,如果删除此点,图不再连通,则此点是割点,反之不是割点(图的连通性一般通过深搜来判定,是否能一次搜索完 全部顶点);
通过深搜优先生成树来判定。从任一点出发深度优先遍历得到优先生成树,对于树中任一顶点V而言,其孩子节点为邻接点。由深度优先生成树可得出两类割点的特性:
(1)若生成树的根有两棵或两棵以上的子树,则此根顶点必为割点。因为图中不存在连接不同子树顶点的边,若删除此节点,则树便成为森林;
(2)若生成树中某个非叶子顶点V,其某棵子树的根和子树中的其他节点均没有指向V的祖先的回边,则V为割点。因为删去v,则其子树和图的其它部分被分割开来。
仍然利用深搜算法,只不过在这里定义visit[v]表示为深度优先搜索遍历图时访问顶点v的次序号,定义low[v]=Min{visit[v],low[w],visit[k]},其中w是顶点v在深度优先生成树上的孩子节点;k是顶点v在深度优先生成树上由回边联结的祖先节点。
割点判定条件:如果对于某个顶点v,存在孩子节点w且low[w]>=visit[v],则该顶点v必为关节点。因为当w是v的孩子节点时,low[w]>=visit[v],表明w及其子孙均无指向v的祖先的回边,那么当删除顶点v后,v的孩子节点将于其他节点被分割开来,从来形成新的连通分量。
#include<stdlib.h>
#include<stdio.h>
#include<stdbool.h>
/*--获取图的割点--*/
//使用邻接表作为图的存储结构
#define MAX_VERTEX_NUM 20 //图的最大顶点个数
typedef char VertexType;
typedef struct ArcNode
{
int adjvex; //边指向的顶点位置
struct ArcNode *nextarc; //指向下一条边的指针
}ArcNode;
typedef struct VNode
{
VertexType data; //顶点名称
ArcNode *firstarc; //指向第一条依附该顶点的边
}VNode,AdjList[MAX_VERTEX_NUM];
typedef struct
{
AdjList vertices;
int vexnum,arcnum; //图的顶点数、边数
}ALGraph;
//创建连通图
//确定图中顶点位置编号
int locateVex(ALGraph alg,char v)
{
int i;
for(i=0;i<alg.vexnum;i++)
{
if(alg.vertices[i].data == v)
return i;
}
return -1;
}
void createALGraph(ALGraph *alg)
{
int i,j,k;
char v1,v2;
ArcNode *p,*s;
printf("输入图的顶点数和边数\n");
scanf("%d %d",&(alg->vexnum),&(alg->arcnum));
getchar();
printf("输入顶点名称\n");
for(k=0;k<alg->vexnum;k++)
{
printf("输入第%d个顶点",k);
scanf("%c",&(alg->vertices[k].data));
alg->vertices[k].firstarc = NULL;
getchar();
}
printf("输入边的两个顶点v1 v2\n");
for(k=0;k<alg->arcnum;k++)
{
printf("输入第%d条边的两个顶点:",k);
scanf("%c %c",&v1,&v2);
getchar();
i = locateVex(*alg,v1);
j = locateVex(*alg,v2);
//创建图为无向图,因此一条边依附两个顶点
p = (ArcNode*)malloc(sizeof(ArcNode));
p->adjvex = j;
p->nextarc = NULL;
if(alg->vertices[i].firstarc == NULL)
{
alg->vertices[i].firstarc = p;
}
else
{
s = alg->vertices[i].firstarc;
while(s->nextarc!=NULL)
s = s->nextarc;
s->nextarc = p;
}
p = (ArcNode*)malloc(sizeof(ArcNode));
p->adjvex = i;
p->nextarc = NULL;
if(alg->vertices[j].firstarc == NULL)
{
alg->vertices[j].firstarc = p;
}
else
{
s = alg->vertices[j].firstarc;
while(s->nextarc!=NULL)
s = s->nextarc;
s->nextarc = p;
}
}
}
//深度优先遍历图
bool visited[MAX_VERTEX_NUM]; //标记图的顶点是否被遍历,已遍历为true,否则为flase;
void DFS(ALGraph G,int v)
{
ArcNode *p;
visited[v] = true;
printf("%c ",G.vertices[v].data);
for(p = G.vertices[v].firstarc;p!=NULL;p = p->nextarc)
{
if(!visited[p->adjvex])
DFS(G,p->adjvex);
}
}
void DFSTraver(ALGraph G)
{
printf("图的深度优先遍历序列:\n");
int i;
for(i=0;i<G.vexnum;i++)
visited[i] = false;
for(i=0;i<G.vexnum;i++)
if(!visited[i])
DFS(G,i);
}
//获取图的关节点(割点)
int visit[MAX_VERTEX_NUM],low[MAX_VERTEX_NUM],count;
void DFSArticul(ALGraph G, int v)
{
int min,w;
ArcNode *p;
visit[v] = min = count++; //v是第count个访问的顶点]
for(p = G.vertices[v].firstarc;p!=NULL;p = p->nextarc) //对v的每个邻接顶点检查
{
w = p->adjvex; //w为v的邻接顶点
if(visit[w]==0) //w未曾访问是v的孩子
{
DFSArticul(G,w); //返回求low[w]
if(low[w] < min)
min = low[w];
if(low[w] >= visit[v])
printf("%d--%c\n",v,G.vertices[v].data);
}else if(visit[w] < min) //w已访问,w是v的生成树上的祖先
{
min = visit[w];
}
}
low[v] = min;
}
void findArticul(ALGraph G)
{
printf("图的关节点(割点):\n");
int i,v;
ArcNode *p;
count = 1;
visit[0] = 1; //设置邻接表上0号顶点为生成树的根
for(i=1;i<G.vexnum;i++)
visit[i] = 0; //其余顶点尚未访问
p = G.vertices[0].firstarc;
v = p->adjvex;
DFSArticul(G,v); //从第v个顶点出发深度优先查找关节点
if(count < G.vexnum) //生成树的根至少有两棵子树
{
printf("0--%c\n",G.vertices[0].data); //根是关节点,输出
while(p->nextarc)
{
p = p->nextarc;
v = p->adjvex;
if(visit[v]==0)
DFSArticul(G,v);
}
}
}
int main()
{
ALGraph alg;
createALGraph(&alg);
DFSTraver(alg);
printf("\n");
findArticul(alg);
printf("\n");
return 0;
}