割点和桥---Tarjan算法

使用Tarjan算法求解图的割点和桥。

1、割点

     主要的算法结构就是DFS,一个点是割点,当且仅当以下两种情况:
        (1)该节点是根节点,且有两棵以上的子树;
        (2)该节点的任一子节点,没有到该节点祖先的反向边(就是说如果没有这个割点,那么这个子节点和那个祖先之间不连通);

void cutpoint_Tarjan(int u,int parent)
{
	int son;  //节点m的儿子节点
	ENode *ptr=(ENode *)malloc(sizeof(ENode));

	dfn[u]=low[u]=depth++;  //访问+标记+遍历
	vis[u]=1;
	ptr=ALG->vlist[u].firstedge;
	while(ptr!=NULL)
	{
		son=ptr->key;
		if(!vis[son])
		{
			DFS(son,u);
			low[u]=MIN(low[u],low[son]);

			if(u==root)    //不同之处//根节点[要定义初始访问节点,因为要考虑割点的2个判断条件]
				cut[u]++;
			else if(u!=root && dfn[u] <= low[son])
				cut[u]++;  //m是割点 
		}
		else if(son != parent)  //有后向边
		{
			low[u]=MIN(low[u],dfn[son]);
		}
		ptr=ptr->next;
	}
}

2、桥

Tarjan算法求割边(桥):
【1】使用(son!=parent && dfn[son]<dfn[u]);

void init_Tarjan(void)
{
	depth=0;
	for(int i=0;i<ALG->n;i++)
	{
		dfn[i]=low[i]=-1;
		vis[i]=0;
	}

	num_bridge=0;
	for(int j=0;j<ALG->e;j++)
	{
		bridge_Node[j].front=0;
		bridge_Node[j].rear =0;
	}
}

void Add_to_BNode(int front,int rear)  //从坐标1开始存储
{
	bridge_Node[num_bridge].front=front;
	bridge_Node[num_bridge].rear =rear;
}

void bridgenode_Tarjan(int u,int parent)
{
	int son;
	ENode *ptr=(ENode*)malloc(sizeof(ENode));

	dfn[u]=low[u]=depth++;  //访问+标记+遍历
	vis[u]=1;
	ptr=ALG->vlist[u].firstedge;
	while(ptr!=NULL)
	{
		son=ptr->key;
		if(son!=parent && dfn[son]<dfn[u])  //避免走重边,效果和id一样
		{
			if(!vis[son])
			{
				bridge_node_Tarjan(son,u);
				low[u]=MIN(low[u],low[son]);
				if(low[son] > dfn[u])  //(u,son)是桥
				{
					num_bridge++;
					Add_to_BNode(u,son);  //存储桥			
				}
			}
			else if(son != parent)
			{
				low[u]=MIN(low[u],dfn[son]);
			}
		}
		ptr=ptr->next;
	}
}
【2】为每一条边标号 id记录每条边(一条无向边拆成的两条有向边id相同),每个点的父亲到它的边的标号;

//结点定义  /*****注意边表节点定义有所变化****/
typedef struct edge_node{
    int key;   //儿子节点[边的终点]
	int id;    //边的编号
	struct edge_node *next;
}ENode;
void init_Tarjan(void)  //Tarjan算法初始化
{
	depth=0;
	for(int i=0;i<ALG->n;i++)
	{
		vis[i]=0;
		dfn[i]=low[i]=-1;
	}
	count_bridge=0;
	for(int j=1;j<=ALG->e;j++)  //取值于1-e
		bridge[j]=0;
}
void bridge_Tarjan(int u,int id)  //id是u的父亲边的编号
{	
	int son;  //u的儿子节点
	ENode *ptr=(ENode *)malloc(sizeof(ENode));

	dfn[u]=low[u]=depth++;  //访问+标记+遍历
	vis[u]=1;
	ptr=ALG->vlist[u].firstedge;
	while(ptr!=NULL)
	{
		if(ptr->id != id)  //避免走重边,相当于cutpoint_Tarjan中的(son != parent)
		{
			son=ptr->key;
			if(!vis[son])
			{
				bridge_Tarjan(son,ptr->id);
				low[u]=MIN(low[u],low[son]);
				if(dfn[u] < low[son])   //注意不取等号,当DFN[u]==LOW[v]时,当u->v dfs递归,存在一条v->u的回边,使得LOW[v]=DFN[u];故不为桥
				{
					bridge[ptr->id]=1;  //第id边是桥
					printf("(%c,%c) ",ALG->vlist[u].vertex,ALG->vlist[son].vertex);  //用于输出割边
				}
			}
			else
			{
				low[u]=MIN(low[u],dfn[son]);
			}
		}
		ptr=ptr->next;
	}
}<span style="font-family:Microsoft YaHei;font-size:18px;color:#3366ff;"> </span>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值