对图的存储结构基于无向图的邻接多重表。
关节点和重连通图:
假若在删去顶点v以及v相关联的各边之后,将图的一个连通分量分割成两个或两个以上的连通分量,则称顶点v为该图的一个关节点。
如果没有关节点的连通图则称为重连通图(双连通图)。
算法实现:
1. 方法一:
最简单也是最直接的算法是,删除一个点然后判断连通性,如果删除此点,图不再连通,则此点是关节点,反之不是关节点(图的连通性一般通过深搜来判定,是否能一次搜索完全部顶点);
2. 方法二:
通过深搜优先生成树来判定。从任一点出发深度优先遍历得到优先生成树,对于树中任一顶点V而言,其孩子节点为邻接点。由深度优先生成树可得出两类邻接点的特性:
(1)若生成树的根有两棵或两棵以上的子树,则此根顶点必为邻接点。因为图中不存在连接不同子树顶点的边,若删除此节点,则树便成为森林;
(2)若生成树中某个非叶子顶点V,其某棵子树的根和子树中的其他节点均没有指向V的祖先的回边,则V为割点。因为删去v,则其子树和图的其它部分被分割开来。
仍然利用深搜算法,只不过在这里定义visit[v]表示为深度优先搜索遍历图时访问顶点v的次序号,也就是第1,2,3…次访问,定义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 <iostream>
#include <stack>
#include <list>
using namespace std;
#define MAX_VERTEX_NUM 20
class EBox {
public:
int ivex, jvex; //该边依附的两个顶点位置
EBox *ilink, *jlink; //分别指向依附这两个顶点的下一条边
};
class VexBox {
public:
int visit; //访问标记
int low; //Low函数标记
string data;
EBox *firstedge; //指向第一条依附该顶点的边
};
class AMLGraph {
public:
VexBox adjmulist[MAX_VERTEX_NUM];
int vexnum, edgenum; //无向图的当前顶点数和边数
};
int LocateVex(VexBox adj[], int num, string v) {
for (int i = 0; i < num; i++) {
if (adj[i].data == v) {
return i;
}
}
}
void CreateGraph(AMLGraph *G) {
cout << "请输入顶点数和弧数: " << endl;
cin >> G->vexnum >> G->edgenum;
cout << "请输入顶点: " << endl;
for (int i = 0; i < G->vexnum; i++) {
cin >> G->adjmulist[i].data;
G->adjmulist[i].firstedge = NULL;
G->adjmulist[i].visit = 0;
G->adjmulist[i].low = 0;
}
for (int i = 0; i < G->edgenum; i++) {
cout << "请输入边的两个顶点: " << endl;
string v1, v2;
cin >> v1 >> v2;
int i1 = LocateVex(G->adjmulist, G->vexnum, v1);
int i2 = LocateVex(G->adjmulist, G->vexnum, v2);
EBox *p = new EBox;
p->ivex = i1;
p->jvex = i2;
p->ilink = G->adjmulist[i1].firstedge;
p->jlink = G->adjmulist[i2].firstedge;
G->adjmulist[i1].firstedge = p;
G->adjmulist[i2].firstedge = p;
}
}
int FirstAdjVex(AMLGraph G, int v) {
int i = v;
if (G.adjmulist[i].firstedge) {
if (G.adjmulist[i].firstedge->ivex == i) {
return G.adjmulist[i].firstedge->jvex;
}
else {
return G.adjmulist[i].firstedge->ivex;
}
}
else
return -1;
}
//v是G中的某个顶点,w是v的邻接顶点
//返回v的(相对于w的)下一个邻接顶点。若w是v的最后一个邻接点,则返回空
int NextAdjVex(AMLGraph G, int v, int w) {
int i1 = v;
int i2 = w;
EBox *p = G.adjmulist[i1].firstedge;
while (p) {
if (p->ivex == i1 && p->jvex != i2) {
p = p->ilink;
}
else {
if (p->ivex != i2 && p->jvex == i1) {
p = p->jlink;
}
else {
break;
}
}
}
if (p && p->ivex == i1 && p->jvex == i2) {
p = p->ilink;
if (p&&p->ivex == i1) {
return p->jvex;
}
else if (p&&p->jvex == i1)
return p->ivex;
}
if (p && p->ivex == i2 && p->jvex == i1) {
p = p->jlink;
if (p&&p->ivex == i1) {
return p->jvex;
}
else if (p&&p->jvex == i1)
return p->ivex;
}
return -1;
}
void DFS(AMLGraph *G, int i) {
cout << G->adjmulist[i].data << endl;
G->adjmulist[i].visit += 1;
for (int w = FirstAdjVex(*G, i); w >= 0; w = NextAdjVex(*G,i,w)) {
if (G->adjmulist[w].visit == 0) {
DFS(G, w);
}
}
}
void DFSTraverse(AMLGraph *G) {
for (int i = 0; i < G->vexnum; i++) {
if (G->adjmulist[i].visit == 0) {
DFS(G, i);
}
}
}
void clearVisited(AMLGraph *G) {
for (int i = 0; i < G->vexnum; i++) {
G->adjmulist[i].visit = 0;
}
}
void DFSArticul(AMLGraph *G, int v0, int *count) {
int min = 0;
G->adjmulist[v0].visit = min = ++(*count); //v是第count个被访问的点
for (int w = FirstAdjVex(*G, v0); w >= 0; w = NextAdjVex(*G, v0, w)) {
if (G->adjmulist[w].visit == 0) {
DFSArticul(G, w, count); //返回前求得low[w]
if (G->adjmulist[w].low < min) {
min = G->adjmulist[w].low;
}
if (G->adjmulist[w].low >= G->adjmulist[v0].visit) {
cout << G->adjmulist[v0].data << endl;
}
}
else if (G->adjmulist[w].visit < min) {
min = G->adjmulist[w].visit;
}
}
G->adjmulist[v0].low = min;
}
void FindArticul(AMLGraph *G) {
int count = 1;
G->adjmulist[0].visit = 1;
int v = FirstAdjVex(*G, 0);
DFSArticul(G, v, &count);
//根是关节点
if (count < G->vexnum) {
cout << G->adjmulist[0].data << endl;
while (v >= 0) {
v = NextAdjVex(*G, 0, v);
if (G->adjmulist[v].visit == 0) {
DFSArticul(G, v, &count);
}
}
}
}
int main () {
AMLGraph g;
CreateGraph(&g);
cout << "深度优先遍历: " << endl;
DFSTraverse(&g);
clearVisited(&g);
cout << "------------------" << endl;
cout << "关节点有: " << endl;
FindArticul(&g);
return 0;
}