首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> C 語言入門

圖論算法實戰:Tarjan 算法求無向圖橋(割邊)完整實現

     編輯:C 語言入門

問題定義

在無向圖中找出所有橋(割邊):刪除後圖連通分量增加的邊。輸出橋的輸入序號。

Tarjan 算法核心原理

  • DFS 時間戳:dfn[u] = 訪問順序,low[u] = 子樹能回溯到的最早祖先
  • 橋判定條件:樹邊 (u,v) 滿足 low[v] > dfn[u] 則為橋
  • 重邊處理:並行邊不構成橋,需標記邊 ID 去重

完整 C++ 實現

#include 
using namespace std;

const int MAXN = 20010;

struct Edge {
    int to, id, num;  // num: 0=正向, 1=反向(用於去重)
    Edge(int a, int b, int c) : to(a), id(b), num(c) {}
};

vector G[MAXN];
int dfn[MAXN], low[MAXN], ans[MAXN], vis[MAXN];
int n, m, dfs_clock, ans_cnt;

void dfs(int u, int parent_edge_id) {
    vis[u] = 1;
    dfn[u] = low[u] = ++dfs_clock;
    
    for (auto &e : G[u]) {
        if (e.num == 1 && e.id == parent_edge_id) continue;  // 跳過反向邊
        
        if (!vis[e.to]) {
            dfs(e.to, e.id);
            low[u] = min(low[u], low[e.to]);
            
            // 橋判定:low[v] > dfn[u] 且非重邊
            if (low[e.to] > dfn[u] && e.num == 0)
                ans[ans_cnt++] = e.id;
        } else {
            // 回邊:更新 low
            low[u] = min(low[u], dfn[e.to]);
        }
    }
}

int tarjan() {
    dfs_clock = ans_cnt = 0;
    memset(vis, 0, sizeof(vis));
    dfs(1, -1);  // 假設圖連通,從 1 開始
    return ans_cnt;
}

int main() {
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &m);
        for (int i=1; i<=n; i++) G[i].clear();
        
        for (int i=1; i<=m; i++) {
            int a, b; scanf("%d%d", &a, &b);
            G[a].push_back(Edge(b, i, 0));  // 正向邊
            G[b].push_back(Edge(a, i, 1));  // 反向邊(同 id,num=1)
        }
        
        int cnt = tarjan();
        sort(ans, ans + cnt);
        for (int i=0; i

關鍵技術點解析

重邊去重策略

  • 每條輸入邊分配唯一 id
  • 正向邊 num=0,反向邊 num=1
  • DFS 時跳過 num==1 && id==parent_edge_id 的反向邊
  • 橋判定時僅考慮 num==0 的樹邊

數組大小設置

  • MAXN = 20010:根據題目節點數上限
  • 邊數組 vector G[MAXN]:鄰接表存儲

多測試用例處理

  • 每輪 G[i].clear() 清空圖
  • 結果輸出後 if (T) printf("\n") 分隔用例

複雜度分析

  • 時間:O(V + E) - 線性掃描所有節點和邊
  • 空間:O(V + E) - 鄰接表 + 輔助數組

常見錯誤避坑

  1. 忘記處理重邊 → 並行邊被誤判為橋
  2. low[u] = min(low[u], dfn[v]) 寫成 low[v] → 回邊處理錯誤
  3. 未初始化 vis/dfn/low 數組 → 多測試用例數據污染
  4. 圖不連通時需對所有未訪問節點調用 dfs()

擴展應用

  • 割點(關鍵節點):low[v] >= dfn[u](根節點特判)
  • 雙連通分量:縮點構建塊樹
  • 強連通分量:有向圖 Tarjan/Kosaraju
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved