zoukankan      html  css  js  c++  java
  • 关节点和重连通分量

    // algo7-3.cpp 实现算法7.10、7.11的程序
    #include"c1.h"
    #define MAX_NAME 2 // 顶点字符串的最大长度+1
    typedef int InfoType;
    typedef char VertexType[MAX_NAME]; // 字符串类型
    #include"c7-21.h" // 邻接表存储结构
    #include"bo7-2.cpp" // 邻接表的基本操作
    int count,lowcount=1; // 全局量count对访问顺序计数,lowcount对求得low值的顺序计数
    int low[MAX_VERTEX_NUM],lowOrder[MAX_VERTEX_NUM];
    // 全局数组,low[]存顶点的low值,lowOrder存顶点求得low值的顺序
    void DFSArticul(ALGraph G,int v0)
    { // 从第v0个顶点出发深度优先遍历图G,查找并输出关节点
    	int min,w;
    	ArcNode *p;
    	visited[v0]=min=++count;
    	// v0是第count个访问的顶点,visited[]是全局变量,在bo7-2.cpp中定义,min的初值为v0的访问顺序
    	for(p=G.vertices[v0].firstarc;p;p=p->nextarc) // 依次对v0的每个邻接顶点检查
    	{
    		w=p->data.adjvex; // w为v0的邻接顶点位置
    		if(visited[w]==0) // w未曾访问,是v0的孩子
    		{
    			DFSArticul(G,w);
    			// 从第w个顶点出发深度优先遍历图G,查找并输出关节点。返回前求得low[w]
    			if(low[w]<min) // 如果v0的孩子结点w的low[]小,这说明孩子结点还与其它结点(祖先)相邻
    				min=low[w]; // 取min值为孩子结点的low[],则v0不是关节点
    			else if(low[w]>=visited[v0]) // v0的孩子结点w只与v0相连,则v0是关节点
    				printf("%d %s
    ",v0,G.vertices[v0].data); // 输出关节点v0
    		}
    		else if(visited[w]<min) // w已访问,则w是v0在生成树上的祖先,它的访问顺序必小于min
    			min=visited[w]; // 故取min为visited[w]
    	}
    	low[v0]=min; // vo的low[]值为三者中的最小值
    	lowOrder[v0]=lowcount++; // 记录v0求得low[]值的顺序(附加),总是在返回主调函数之前求得low[]
    }
    void FindArticul(ALGraph G)
    { // 连通图G以邻接表作存储结构,查找并输出G上全部关节点。全局量count对访问计数。算法7.10
    	int i,v;
    	ArcNode *p;
    	count=1; // 访问顺序
    	visited[0]=count; // 设定邻接表上0号顶点为生成树的根,第1个被访问
    	for(i=1;i<G.vexnum;++i)
    		visited[i]=0; // 其余顶点尚未访问,设初值为0
    	p=G.vertices[0].firstarc; // p指向根结点的第1个邻接顶点
    	v=p->data.adjvex; // v是根结点的第1个邻接顶点的序号
    	DFSArticul(G,v); // 从第v顶点出发深度优先查找关节点
    	if(count<G.vexnum) // 由根结点的第1个邻接顶点深度优先遍历G,访问的顶点数少于G的顶点数
    	{ // 说明生成树的根有至少两棵子树,则根是关节点
    		printf("%d %s
    ",0,G.vertices[0].data); // 根是关节点,输出根
    		while(p->nextarc) // 根有下一个邻接点
    		{
    			p=p->nextarc; // p指向根的下一个邻接点
    			v=p->data.adjvex;
    			if(visited[v]==0) // 此邻接点未被访问
    				DFSArticul(G,v); // 从此顶点出发深度优先查找关节点
    		}
    	}
    }
    void main()
    {
    	int i;
    	ALGraph g;
    	printf("请选择无向图
    ");
    	CreateGraph(g); // 构造无向图g
    	Display(g); // 输出无向图g
    	printf("输出关节点:
    ");
    	FindArticul(g); // 求连通图g的关节点
    	printf(" i G.vertices[i].data visited[i] low[i] lowOrder[i]
    "); // 输出辅助变量
    	for(i=0;i<g.vexnum;++i)
    		printf("%2d %9s %14d %8d %8d
    ",i,g.vertices[i].data,visited[i],low[i],lowOrder[i]);
    }

    代码的运行结果:

    请选择无向图
    请输入图的类型(有向图:0,有向网:1,无向图:2,无向网:3): 2(见图759)
    请输入图的顶点数,边数: 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
    无向图(见图760)
    13个顶点:
    A B C D E F G H I J K L M
    17条弧(边):
    A→L A→F A→C A→B
    B→M B→H B→G B→D B→C
    D→E
    G→K G→I G→H

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

    8 I 9 8 2
    9 J 4 2 1
    10 K 7 5 4
    11 L 2 1 11
    12 M 3 1 10



    运行algo7-3.cpp 构造的深度优先生成树如图761 所
    示。图761 和图759 的拓扑结构是一样的。5 条用虚线
    表示的边是构造深度优先生成树多余的边,它们是连接祖先
    的回边。如果一个结点不仅有连向双亲的边,还有连向祖先
    的回边。则对这个结点来说,它的双亲结点不是关节点。如
    图761 中的结点B,它有连向双亲结点M 的边,还有连向
    祖先结点A 的边。这样,如果删除结点M,B 仍然与图的其
    它部分连通(重连通)。而图761 中的结点I,它只有连向双
    亲结点G 的边。一旦结点G 被删除,结点I 就与图的其它部
    分不连通,也就是一个连通分量被分割成了多个连通分量。
    结点G 被称为关节点。


    如何确定关节点?算法7.10、7.11 的思路是这样的:首
    先在深度优先遍历图时,不仅标注某顶点是否被访问,还标
    注它的访问顺序。visited[]不再只是FALSE 和TRUE,而是1~顶点数。由于采用深度优先
    遍历,某结点的祖先被访问的顺序必先于该结点被访问的顺序。仍以图761 为例,由第
    1 个结点A 深度优先遍历的顺序是:A、L、M、J、B、⋯⋯ 增加1 个辅助数组low[],
    对顶点v,定义low[v]=min(visited[v],low[w],visited[k])。其中w 和k 分别是v 的孩子
    和由回边相连的祖先。由算法7.11 可知,low[]是在递归调用返回之前求得的。所以,求
    得low[]的顺序是:⋯⋯ B、M、L、⋯⋯ 也就是说,孩子的low[]是先于双亲的low[]而
    获得的。这可由程序运行结果中的lowOrder[]看出(增加辅助数组lowOrder[]的目的就是
    帮助分析求得low[]的顺序)。
    如果顶点v 有孩子w,且有low[w]≥visited[v],则顶点v 必为关节点。下面分析几
    种可能存在的情况:
    (1) 如果顶点v 有通过回边相连的祖先k,则low[v]=visited[k](祖先顶点k 被访问的
    顺序)。同时k 也是v 的双亲u 的祖先或双亲,故有low[v]=visited[k]<visited[u](结点祖
    先或双亲必先于该结点被访问)。不满足判定关节点的公式,故u 不是v 的关节点。这种
    情况如图761 中顶点B 的low[]=顶点A 的visited[]=1,其双亲M 的visited[]=3,故M
    不是B 的关节点。
    (2) 如果顶点v 没有通过回边相连的祖先,但有孩子w,而孩子顶点w 有通过回边相
    连的祖先k,则low[w]=visited[k],而k 也是v 的双亲u 的祖先,仍有visited[k]≤
    visited[u]。如顶点K 没有通过回边相连的祖先,但有孩子G,而G 有通过回边相连的祖
    先B。顶点G 的low[]等于顶点B 的visited[]=5,也等于顶点K 的low[]。而K 的双亲H
    的visited[]=6,故H 不是K 的关节点。

    (3) 如果顶点v 既无孩子又无通过回边相连的祖先,则其双亲结点u 是关节点。在这

    种情况下,low[v]=visited[v](顶点v 被访问的顺序)。而u 被访问的顺序必定小于v 的,
    故有low[v]=visited[v]>visited[u]。所以u 是v 的关节点。如顶点E 就是既无孩子又无通
    过回边相连的祖先,则其双亲结点D 是E 的关节点。
    (4) 如果顶点v 没有通过回边相连的祖先,虽有孩子顶点w,但w 也没有通过回边相
    连的祖先,则v 的双亲结点u 是关节点。在这种情况下,low[w]= visited[w](顶点w 被访
    问的顺序)>low[v]=visited[v](顶点v 被访问的顺序)>visited[u],故u 是v 的关节点。如
    顶点D 虽有孩子顶点E,但E 没有通过回边相连的祖先,则low[D]=5=visited[B]。故B
    是D 的关节点。
    通过low[w]≥visited[v]来判断连通图关节点的方法不能用于根结点。因为根结点的
    visited[]=1,是最小值。判断根结点是否为关节点要看它有几棵子树,如果超过1 棵,则
    根结点就是关节点。原因是,它的每棵子树上的结点都和其它子树的不相连。否则在深度
    优先遍历其它子树时,就会遍历到,也就不成为根结点的子树了。所以算法7.10 在深度
    优先遍历时,不是直接从根结点遍历,而是从根结点的第1 个邻接顶点开始遍历。当遍历
    完这个邻接顶点的生成子树,若还有顶点没被访问,则说明根结点是关节点。如图761
    所示,对根结点A 的第1 棵子树L 遍历结束后,A 还有邻接点F 没被访问到。说明除根结
    点A 之外,L 子树上的任何一个结点都不和F 邻接。这样,若根结点A 被删除,原图就会
    被分割成L 子树和F 两部分。故根结点A 是关节点。
    运行algo7-3.cpp 在输出关节点时,B 被输出了2 次。其原因是删除B 使连通图分割
    成3 个连通分量。

  • 相关阅读:
    [转载]写作经验谈--如何写一本书?
    决定写一本书
    c中自定义函数通过sizeof来输出数组的长度为何不正确?【原创】
    [转]关于PHP的漏洞以及如何防止PHP漏洞?
    [转]PHP安全之防止你的源代码或重要配置信息暴露在外
    PHPUnit 单元测试框架(鸡肋)
    [转]避免PHP-FPM内存泄漏导致内存耗尽
    [转]PHP ffmpeg截取视频指定帧为图片,获取rotation信息并旋转
    ThinkPHP 缓存 以及Zend OPCache提升PHP性能
    简单测漏 语句
  • 原文地址:https://www.cnblogs.com/KongkOngL/p/4074453.html
Copyright © 2011-2022 走看看