您的位置:首页 > 理论基础 > 数据结构算法

数据结构之图的关节点和重连通分量

2014-10-16 19:31 781 查看
本着业界良心,我感觉这个链接中关于图的关节点讲得很不错。什么是关节点?在某图中,若删除顶点V以及V相关的边后,图的一个连通分量分割为两个或两个以上的连通分量,则称顶点V为该图的一 个关节点。



如图所示,图中有四个关节点A/B/D和G,如顶点B和它的边被删除,图就会被分为3个连通分量A,C,F,L,M,J;G,H,I,K;D,E.一个没有关节点的连通图称为重连通图,上图显然不是重连通图

利用深度优先搜索便可以求的图的关节点,本由此可判别图是否重连通。

  上图右边是从顶点A出发深度优先搜索遍历所得的深度优先生成树。对于树中任一顶点V而言,其孩子节点为邻接点。由深度优先生成树可得出两类关节点的特性:

  (1)若生成树的根有两棵或两棵以上的子树,则此根顶点必为关节点。因为图中不存在连接不同子树顶点的边,若删除此节点,则树便成为森林。

  (2)若生成树中某个非叶子顶点V,其某棵子树的根和子树中的其他节点均没有指向V的祖先的回边,则V为关节点。因为删去v,则其子树和图的其它部分被分割开来

  

若对图Graph=(V,{Edge}) 重新定义遍历时的访问函数visited,并引入一个新的函数low,则由一次深度优先遍历便可求得连通图中存在的所有关节点。

定义visited[v]为深度优先搜索遍历连通图时访问顶点v 的次序号;定义:



对于某个顶点v,存在孩子结点w 且low[w]≧visited[v],则该顶点v 必为关节点。因为当w 是v 的孩子结点时,low[w]≧visited[v],表明w 及其子孙均无指向v 的祖先的回边。由定义可知,visited[v]值即为v 在深度优先生成树的前序序列的序号,只需将DFS 函数中头两个语句改为visited[v0]=++count(在DFSTraverse 中设初值count=1)即可;low[v]可由后序遍历深度优先生成树求得,而v
在后序序列中的次序和遍历时退出DFS 函数的次序相同,由此修改深度优先搜索遍历的算法便可得到求关节点的算法.

代码如下(注释比较详细了):

/*
* @description:求关节点,关节点的定义;假如删除顶点v及相关各边后,将图的一个
联通分量分割成两个或是两个以上的联通分量,则顶点v为图的关节点
* @more:注意根节点的情况是需要单独处理的,其关节点成立的条件是:
生成树的根有两棵或两颗以上的子树
因为图中不存在连接不同子树的根节点的边,这样在遍历子树的过程,
会存在有节点子树是无法遍历到的,这样把根节点删除后,就会把图分成
森林
*/
void FindArticul(ALGraph G) {
int i,v;
ArcNode *p;

count = 1;
low[0] = visited[0] = 1;	//表示邻接表上0号顶点为生成树的根

//标志其他的节点都没有访问
for(i = 1; i < G.vexnum ; i++)
visited[i] = FALSE;

p = G.vertices[0].firstarc;
v = p->adjvex;

DFSArticul(G,v);	//从第一个邻接点开始进行查找

if(count < G.vexnum ) {
//根节点为关节点,输出
printf("%d,%d\n",0,G.vertices[0].data);
//继续往下找
while(p->nextarc) {
p = p->nextarc;
v = p->adjvex;
if(visited[v] == 0)
DFSArticul(G,v);
}
}

}

/*
* @descirption:从第v的节点出发深度优先遍历图查找图的关节点
* @more:注意visited[]现在不再是单纯的访问标志(0/1)而是1/vexnum-1,
是通过深度优先搜索遍历得到的,且在遍历的过程的中生成树中
父亲节点的总是并孩子节点先遍历到
而low[]是类似后序遍历得到的,也就是说孩子节点是先于父亲节点
遍历到的
*/
void DFSArticul(ALGraph G,int v) {
int min,w;
ArcNode *p;

//v是第count访问的节点
visited[v] = min = ++count;
//对v的每个邻接点进行遍历
for(p = G.vertices[v].firstarc ; p ; p = p->nextarc ) {
//w是v的邻接点
w = p->adjvex;

//w未曾访问,是v的孩子
if(visited[w] == 0) {
//返回前求得low[w]
DFSArticul(G,w);
/*如果v的孩子节点的low小,说明其还有祖先节点和孩子节点相连,
这也是为什么上面要先返回low[w]的原因
*/
if(low[w] < min)
min = low[w];

if(low[w] >= visited[v])
printf("%d,%d\n",v,G.vertices[v].data);
}
//w已经访问过,w是v0在生成树上的祖先
else if(visited[w] < min)
min = visited[w];
}

/*
v的节点的low为visited[v]/low[w]/visited[k]中最小的
w是顶点v的在深度优先生成树上的孩子节点
k是顶点v在深度优先生成树有回边连接的祖先节点
*/
low[v] = min;

Order_Low[v] = lowcount++;	//用于理解
}


贴个测试代码:

/*---------------------------------------------------------------------------
* file:ALGraph.c
* date:10-14-2014
* author:doodlesomething@163.com
* version:1.0
* description:邻接表实现图的基本操作及求图的关节点
----------------------------------------------------------------------------*/

#include <stdio.h>
#include "findarticul.h"

int main() {
int i;
ALGraph G;

//创建图
CreateGraph(&G);

//深度优先遍历图
DFSTraverse(G,PrintElem);

printf("\n");

//求关节点
FindArticul(G);

printf("i	G.vertices[i].data	visited[i]	low[i]	Order_Low\n");
for(i = 0; i < G.vexnum ; i++)
printf("%d	%d	%d	%d	%d\n",i,G.vertices[i].data,visited[i],low[i],Order_Low[i]);

return 0;

/*
please enter the kind of graph(DG:0,DN:1,UDG:2,UDN:3):2
please the vexnum and arcnum:13,17
please enter the value of each vertex:1,2,3,4,5,6,7,8,9,10,11,12,13
please enter the heads and tails:
1,2
1,3
1,6
1,12
2,3
2,4
2,7
2,8
2,13
4,5
7,8
7,9
7,11
8,11
10,12
10,13
12,13

>>(可以看到2输出了两次,这是因为删除2会将图分割成三颗树)
1	2	3	4	5	7	8	11	9	13	10	12	6
6,7
1,2
3,4
1,2
0,1
i	G.vertices[i].data	visited[i]	low[i]	Order_Low
0	1	1	1	0
1	2	5	1	9
2	3	12	1	8
3	4	10	5	7
4	5	11	10	6
5	6	13	1	12
6	7	8	5	3
7	8	6	5	5
8	9	9	8	2
9	10	4	2	1
10	11	7	5	4
11	12	2	1	11
12	13	3	1	10

*/
}


完整的源码看:GitHub
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: