2021-11-25【数据结构/严蔚敏】【关节点&重连通分量】【代码实现算法7.10-7.11】

知识点

假若在删去顶点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语言学习必备】学习

本课件是一个动态演示数据结构算法执行过程的辅助教学软件, 它可适应读者对算法的输入数据和过程执行的控制方式的不同需求, 在计算机的屏幕上显示算法执行过程中数据的逻辑结构或存储结构的变化状况或递归算法执行过程中栈的变化状况。整个系统使用菜单驱动方式, 每个菜单包括若干菜单项。每个菜单项对应一个动作或一个子菜单。系统一直处于选择菜单项或执行动作状态, 直到选择了退出动作为止。 本系统内含84个算法,分属13部分内容,由主菜单显示,与《数据结构》教科书中自第2章至第11章中相对应。各部分演示算法如下: 1. 顺序表 (1)在顺序表中插入一个数据元素(ins_sqlist) (2)删除顺序表中一个数据元素(del_sqlist) (3)合并两个有序顺序表(merge_sqlist) 2. 链表 (1)创建一个单链表(Crt_LinkList) (2)在单链表中插入一个结点(Ins_LinkList) (3)删除单链表中的一个结点(Del_LinkList) (4)两个有序链表求并(Union) (5)归并两个有序链表(MergeList_L) (6)两个有序链表求交(ListIntersection_L) (7)两个有序链表求差(SubList_L) 3. 栈和队列 (1)计算阿克曼函数(AckMan) (2)栈的输出序列(Gen、Perform) (3)递归算法的演示  汉诺塔的算法(Hanoi)  解皇后问题的算法(Queen)  解迷宫的算法(Maze)  解背包问题的算法(Knap) (4)模拟银行(BankSimulation) (5)表达式求值(Exp_reduced) 4. 串的模式匹配 (1)古典算法(Index_BF) (2)求Next 函数值(Get_next)和按Next 函数值进行匹配 (Index_KMP(next)) (3)求 Next 修正值(Get_nextval)和按 Next 修正值进行匹配(Index_KMP(nextval)) 5. 稀疏矩阵 (1)矩阵转置 (Trans_Sparmat) (2)快速矩阵转置 (Fast_Transpos) (3)矩阵乘法 (Multiply_Sparmat) 6. 广义表 (1)求广义表的深度(Ls_Depth) (2)复制广义表(Ls_Copy) (3)创建广义表的存储结构(Crt_Lists) 7. 二叉树 (1)遍历二叉树  二叉树的线索化  先序遍历(Pre_order)  中序遍历(In_order)  后序遍历(Post_order) (2) 按先序建二叉树(CrtBT_PreOdr) (3) 线索二叉树  二叉树的线索化  生成先序线索(前驱或后继) (Pre_thre)  中序线索(前驱或后继) (In_thre)  后序线索(前驱或后继) (Post_thre)  遍历中序线索二叉树(Inorder_thlinked)  中序线索树的插入(ins_lchild_inthr)和删除(del_lchild_inthr)结点 (4)建赫夫曼树和求赫夫曼编码(HuffmanCoding) (5)森林转化成二叉树(Forest2BT) (6)二叉树转化成森林(BT2Forest) (7)按表达式建树(ExpTree)并求值(CalExpTreeByPostOrderTrav) 8. 图 (1)图的遍历  深度优先搜索(Travel_DFS)  广度优先搜索(Travel_BFS) (2)求有向图的强连通分量(Strong_comp) (3)有向无环图的两个算法  拓扑排序(Toposort)  关键路径(Critical_path) (4)求最小生成树  普里姆算法(Prim)  克鲁斯卡尔算法(Kruscal) (5)求关节点连通分量(Get_artical) (6)求最短路径  弗洛伊德算法(shortpath_Floyd)  迪杰斯特拉算法(shortpath_DIJ) 9. 存储管理 (1)边界标识法 (Boundary_tag_method) (2)伙伴系统 (Buddy_system) (3)紧缩无用单元 (Storage_compaction) 10. 静态查 (1)顺序查(Search_Seq) (2)折半查 (Serch_Bin) (3)插值查 (Search_Ins) (4)斐波那契查 (Search_Fib) (5)次优查树(BiTree_SOSTree) 11. 动态查 (1)在二叉排序树上进行查(bstsrch)、插入结点(ins_bstree)和删除结点(del_bstree) (2)在二叉平衡树上插入结点(ins_AVLtree) 和删除结点(del_AVLtree) (3)在 B-树上插入结点(Ins_BTree) 和 删除结点(Del_BTree) (4)在 B+树上插入结点(Ins_PBTree) 和 删除结点(Del_PBTree) 12. 内部排序 (1)简单排序法  直接插入排序(Insert_sort)  表插入排序(内含插入(Ins_Tsort) 排(Arrange)两个算法)  起泡排序(BubbleSort)  简单选择排序(SelectSort) (2)复杂排序法  堆排序(HeapSort)  快速排序(QuickSort)  锦标赛排序(Tournament) (3)其他  快速地址排序(QkAddrst)  基数排序(RadixSort) 13. 外部排序 (1)多路平衡归并排序(K-Merge) (2)置换-选择排序(Repl_Selection)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eternity_GQM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值