C语言连通图的关节点——严蔚敏C语言《数据结构》

关节点

倘若在删去顶点v以及v相关联的各边后,将图的一个连通分量分割成两个或两个以上的分量,则称顶点v为该图的一个关节点

关节点的特性

由深度优先生成树可得出两类关节点的特性:

  1. 若生成树的根有两棵或两棵以上的子树,则此根顶点必为关节点
  2. 若生成树中某个非叶子顶点v,其某棵子树的根和子树中的其他结点均没有指向v的祖先的回边,则v为关节点

连通图及深度优先生成树

相关参数

  1. visited[v]为深度优先搜索遍历连通图时访问顶点v的次序号
  2. low[v]可以理解为顶点v可以往回找到的最远祖先序号

数据

13 17
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 

实现代码

#include<stdio.h>
#include<stdlib.h>
#define maxSize 100

typedef struct ArcNode
{
	int adjvex;
	int weight;
	struct ArcNode *nextarc;
}ArcNode;
typedef struct VNode
{
	char data;
	ArcNode *firstarc;
}VNode;
typedef struct AGraph
{
	VNode adjlist[maxSize];
	int numNodes, numEdges;
}AGraph;

int FindVex(AGraph *g, char ch)
{
	for(int i = 0;i < g->numNodes;i++)
		if(g->adjlist[i].data == ch)
			return i;
}

AGraph *create_graph()
{
	int i, j, k;
	ArcNode *pe;
	AGraph *g;
	g = (AGraph*)malloc(sizeof(AGraph));

	FILE *fp = fopen("data.txt", "r");

	fscanf(fp, "%d %d", &g->numNodes, &g->numEdges);
	printf("图的顶点个数为%d,不重复边数为%d\n", g->numNodes, g->numEdges);

	for(i = 0;i < g->numNodes;i++)
	{
		g->adjlist[i].firstarc = NULL;
	}

	for(i = 0;i < g->numNodes;i++)
		fscanf(fp, "%s", &g->adjlist[i].data);
	
	char x, y;

	for(k = 0;k < g->numEdges;k++)
	{
		fscanf(fp, "%s %s", &x, &y);
		//printf("已初始化边(%d, %d)", i, j);

		int i = FindVex(g, x);
		int j = FindVex(g, y);

		pe = (ArcNode*)malloc(sizeof(ArcNode));

		pe->adjvex = j;
		pe->nextarc = g->adjlist[i].firstarc;		//每一个边表分为 adjvex和nextarc两块	第一次添加相当于将边表的nextarc置空,然后邻接表的firstarc连上边表
		g->adjlist[i].firstarc = pe;				//第二次添加就相当于是头插法,将nextarc指向此时邻接表的firstarc,然后连上邻接表

		//对于无向图
		pe = (ArcNode*)malloc(sizeof(ArcNode));

		pe->adjvex = i;
		pe->nextarc = g->adjlist[j].firstarc;
		g->adjlist[j].firstarc = pe;
	}
	fclose(fp);
	return g;
}

//图的深度遍历
void DFS(AGraph *g, int v, int visit[])
{
	visit[v] = 1;
	printf("%c ", g->adjlist[v].data);
	ArcNode *p = g->adjlist[v].firstarc;
	while(p!=NULL)
	{
		if(visit[p->adjvex]==0)
			DFS(g, p->adjvex, visit);
		p=p->nextarc;
	}
}

int visited[maxSize]={0};
int count = 0;
int low[maxSize] = {0};

void DFSArticul(AGraph *g, int v0)
{	//从第v0个顶点出发深度遍历优先遍历图g,查找并输出关节点
	int min = ++count;
	visited[v0] = min;		//v0是第count个访问的顶点
	ArcNode *p;
	for(p = g->adjlist[v0].firstarc; p ; p = p->nextarc)		//对v0的每个邻接顶点检查
	{
		int 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])	//low[w]大于等于visited[v0],说明邻接顶点w的指向的上一级仍然比v0大
				printf("%d %c\n", v0, g->adjlist[v0].data);			//即v0子树上存在一个结点,该结点均没有指向w的祖先的回边
		}
		else if(visited[w] < min)	//w已访问,w是v0在生成树上的祖先
		{
			min = visited[w];
		}
	}
	low[v0] = min;			
}

void FindArticul(AGraph *g)
{	//连通图G以邻接表作存储结构,查找并输出G上全部关节点。

	count = 1;
	int i;
	visited[0] = 1;				//设定邻接表上0号顶点为生成树的根
	for(i = 1;i < g->numNodes;i++)
		visited[i] = 0;
	ArcNode *p = g->adjlist[0].firstarc;
	int v = p->adjvex;
	DFSArticul(g, v);				//从第v顶点出发深度优先查找关节点
	if(count < g->numNodes)			//生成树的根有至少两棵子树
	{
		printf("0 %c\n", g->adjlist[0].data);		//根是关节点,输出
		while(p->nextarc)
		{
			p = p->nextarc;
			v = p->adjvex;
			if(visited[v]==0)
				DFSArticul(g, v);
		}//while
	}//if
}//FindArticul

int main()
{
	AGraph *G;
	int visit[maxSize] = {0};

	G = create_graph();

	printf("深度遍历结果: \n");
	DFS(G, 0, visit);
	printf("\n");

	printf("\n关节点: \n");
	FindArticul(G);

	printf("\nG.adjlist[i].data\t");
	for(int i = 0;i<G->numNodes;i++)
		printf("%3c ", G->adjlist[i].data);
	printf("\nvisited[i]\t\t");
	for(int i = 0;i<G->numNodes;i++)
		printf("%3d ", visited[i]);
	printf("\nlow[i]\t\t\t");
	for(int i = 0;i<G->numNodes;i++)
		printf("%3d ", low[i]);

	getchar();
	return 0;

}

运行结果

在这里插入图片描述

  • 6
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值