1. 程式人生 > >割點和橋---Tarjan演算法

割點和橋---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>