桥的定义:
在图论中,一条边被称为“桥”代表这条边一旦被删除,这张图的连通块数量会增加。等价地说,一条边是一座桥当且仅当这条边不在任何环上。一张图可以有零或多座桥。
理论:
从某个顶点开始进行DFS,并按访问先后顺序进行标号
如果某个结点的某条边的DFS中有某个孩子的边指向了这个结点或这个结点的祖先,那么这条边不为桥,反之则为桥
如上图,顶点1的孩子结点中,有顶点3的绿色边指向了顶点1的祖先顶点0,那么绿色边一定不为桥
如上图,顶点5的孩子结点中并没有指向顶点5的祖先结点的边,所以蓝色边一定为桥
依据上面的理论,可进行下面的实现:
设置一个visit数组表示访问的顺序
设置一个low数组,low[i]表示结点i的孩子中指向的最早的祖先
low[v] = min { visit[v], low[w], visit[k] }
其中:w为v的孩子结点
k为v的祖先结点
若(v1,v2)是一条边
low[v1]>visit[v2]的意义:
v1包括v1的子树所连的最早的祖先在v2的子树中
结论:
若low[v1]>visit[v2],则(v1,v2)必为桥
图示举例:
图1、样例图
如图1,我们要找到图中所有的桥
图2、计算visit数组和low数组的过程
如图2,从结点0开始做DFS
结点左边的数为visit,右边的数为low
所得出visit的顺序和low的顺序由上图的蓝色箭头所示
图3、low大于visit的结点
如图3,绿色箭头所示的low都大于visit,所以所对应的边都为桥
代码(图采用邻接表存储):
class Map{
private:
int Node_num;
struct Node *m;
int bridge_count = 0;
int *visited, *low;
bool *is_visit;
int num = 1;
int *father;
public:
Map(){};
~Map(){};
void setMap();
void one_DFS(int i, int father);
void One_DFS_get_bridge();
};
void Map::One_DFS_get_bridge()
{
bridge_count = 0;
for (int i = 0; i<Node_num; i++)
is_visit[i] = false;
// cout<<"Tarjan算法的桥:"<<endl;
for (int i = 0; i<Node_num; i++)
{
if (is_visit[i] == false)
one_DFS(i, i);
}
// cout << "Tarjan算法桥的数量:" << bridge_count << endl;
}
void Map::one_DFS(int i, int father)
{
is_visit[i] = true;
visited[i] = num;
low[i] = num;
num++;
struct Node *p;
p = m[i].next->next;
while (p != NULL)
{
if (p->info == father)
{
p = p->next;
continue;
}
if (is_visit[p->info] == false)
{
one_DFS(p->info, i);
if (low[p->info]<low[i])
low[i] = low[p->info];
if (low[p->info]>visited[i])
{
// cout<<p->info<<' '<<i<<endl;
bridge_count++;
}
}
else
{
if (visited[p->info] < low[i])
low[i] = visited[p->info];
}
p = p->next;
}
}