Tarjan算法求桥

桥的定义:

在图论中,一条边被称为“桥”代表这条边一旦被删除,这张图的连通块数量会增加。等价地说,一条边是一座桥当且仅当这条边不在任何环上。一张图可以有零或多座桥。

理论:

从某个顶点开始进行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;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值