割點和橋---Tarjan演算法
阿新 • • 發佈:2019-02-07
使用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]);
【2】為每一條邊標號 id記錄每條邊(一條無向邊拆成的兩條有向邊id相同),每個點的父親到它的邊的標號;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; } }
//結點定義 /*****注意邊表節點定義有所變化****/
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>