1. 程式人生 > >Tarjan演算法求割點和割邊

Tarjan演算法求割點和割邊

目錄

名詞解釋

Tarjan演算法

割點求解:

割邊求解:

參考部落格


名詞解釋

割點:在無向圖中,刪除某個節點後,圖的連通分量數量增加,則稱該節點為割點

橋:如果刪除某條邊後,連通圖變得不再連通,則此條邊為橋,或者為割邊

Tarjan演算法

在Tarjan演算法中,有兩個十分重要的陣列,dfn陣列,low陣列

dfn陣列:表示dfs遍歷到該節點的序號,也就是順序值

low陣列:表示當前頂點不通過父親節點能訪問到的祖先節點(父親節點上面的節點)中的最小順序值

割點求解:

如果,至少存在一個兒子節點必須要經過父親節點才能訪問到祖先節點,那麼這個父親節點即為割點,假設父親節點為u,兒子節點為v,那麼滿足:low[v] >= dfn[u] 說明:節點u為割點

,但是仍然存在一種情況,當u為根節點時,所有兒子節點的low[v] 一定滿足大於等於dfn[u],所以我們必須要分開討論:如果,根節點必須要有兩個兒子節點,就可以說明跟節點為割點

割邊求解:

相同,割邊的話,兒子節點不經過這條邊就可以訪問到祖先節點,那麼就要滿足:low[v] > dfn[u],如果low[v] == dfn[u],節點v還可以通過其他路徑可以回到u,但是隻包含條件low[x] > dfn[u]時,說明沒有任何一條路可以出了u-v邊外,由u到達v,此時說明,u-v之間的邊為一條割邊

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>

using namespace std;
typedef long long ll;
const int maxn = 10000;
int low[maxn],dfn[maxn],head[maxn];
int n,m,tot,k,root;
bool flag[maxn];
vector<pair<int,int> > bridge;
struct Node
{
    int next,v;
}edge[maxn];

void Add_edge(int x,int y)
{
    edge[k].v = y;edge[k].next = head[x];head[x] = k++;
    edge[k].v = x;edge[k].next = head[y];head[y] = k++;
}

void Tarjan(int x,int father)
{
    int child = 0;
    dfn[x] = low[x] = ++tot;
    for(int i = head[x];i != -1;i = edge[i].next)
    {
        int v = edge[i].v;
        if(!dfn[v])
        {
            child ++;
            Tarjan(v,x);
            low[x] = min(low[x],low[v]);
            if(x != root && dfn[x] <= low[v]) flag[x] = 1;    //表示當前節點為割點
            if(x == root  && child == 2) flag[x] = 1;
            if(low[v] > dfn[x]) bridge.push_back(make_pair(x,v));
        }
        else if(v != father)
            low[x] = min(dfn[v],low[x]);
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    int x,y;
    scanf("%d%d",&n,&m);
    for(int i = 0;i < m;i ++)
    {
        scanf("%d%d",&x,&y);
        Add_edge(x,y);
    }
    root = 1;         //在求解割點、割邊的時候首先確保圖為連通圖
    Tarjan(1,root);
    printf("圖中的割點為:\n");
    for(int i  = 1;i <= n ;i ++)
        if(flag[i])
            printf("%d ",i);
    printf("\n");
    printf("圖中的割邊為:\n");
    for(int i = 0;i < bridge.size();i ++)
        printf("%d -- %d\n",bridge[i].first,bridge[i].second);
    printf("\n");
    return 0;
}
/*
6 7
1 4
1 3
4 2
3 2
2 5
2 6
5 6
圖中的割點為:
2
圖中的割邊為:

*/

參考部落格

https://blog.csdn.net/wtyvhreal/article/details/43530613