首先要明白dfs树的概念
dfs生成树是指在一个图中,选取任意一个节点,依据dfs算法遍历节点得到的一棵生成树。由上述定义可知,该树不唯一。
先把dfs生成树的代码展示如下(关于dfs生成树的五种边,见):

#include<bits/stdc++.h>
using namespace std;

vector<vector<int>> adj;  // 邻接链表表示图
vector<bool> visited;  // 访问过
vector<int> parent;  // 父节点

vector<pair<int, int>> tree_edges;
vector<pair<int, int>> back_edges;  // 对于无向图,dfs生成树只需要记录树边和回边

void dfs_build_tree(int u, int fa) {
    visited[u] = true;
    parent[u] = fa;        // 记录父节点
    
    for (int v : adj[u]) {
        if (!visited[v]) {
            tree_edges.push_back({u, v});  // 记录树边
            dfs_build_tree(v, u);          // 递归
        } else if (v != fa) {
            back_edges.push_back({u, v});  // 记录回边
        }
    }
}
// 只生成树,不进行分析

然后是tarjan_scc算法,这个算法在构建一棵dfs生成树的同时还求出了最大连通分量,为此要维护以下几个参数:
1.int dfn[MAXN],用于记录构建过程中节点发现时间
2.int low[MAXN],用于记录最早能回溯到的祖先的时间戳
3.计数器int timestamp

4.tarjan栈 stack stk
5.入栈标志 bool inStack[MAXN]

6.int scc_id[MAXN] 节点的scc编号,也就是说这个节点属于哪个scc
7.int scc_cnt scc计数器

因而在dfs生成树的基础上修改,有以下代码:

#include<bits/stdc++.h>
using namespace std;

const int MAXN;
int dfn[MAXN], low[MAXN];
int timestamp = 0;
stack<int> stk;  // tarjan栈
bool inStack[MAXN];  // 节点是否在栈中

int scc_id[MAXN];  // 节点所属的scc编号
int scc_cnt = 0;  // scc计数器 

vector<vector<int>> adj;
vector<vector<int>> new_adj; 

void tarjan(int u){
  dfn[u] = low[u] = ++timestamp;  // 依据时间戳初始化dfn和low的值
  stk.push(u);
  inStack[u] = true;

  for(int v : adj[u]){
    if(!dfn[v]){  // 相当于先前的!visited[v]
      tarjan(v);
      low[u] = min(low[u], low[v]);  // 有向图中找前后两项最小的时间戳
      // 类似的这里也可以加上push_back入树边vector
    } else if(inStack[v]){  // v在栈中,说明v是u的祖先,(u,v)是回边
      low[u] = min(low[u], dfn[v]);
    }
  }

  // 在遍历完当下节点后,判断是否遍历完当前强连通分量
  if(dfn[u] == low[u]){
    scc_cnt++;
    int v;
    do{
      v = stk.top(); stk.pop(); inStack[v] = false;  // 取出栈顶
      scc_id[v] = scc_cnt;  // 标记归属
    }while(v != u);
  }
}

注意:因为DFS只能向下访问到连通的部分,一次tarjan不一定能完成全部节点的处理,在主循环调用的时候,应当这样写:
for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);

通过tarjan_scc得到强连通分量,我们可以对图进行缩边/缩点的操作,去掉原图里的环,得到有向无环图。

posted on 2026-01-21 15:17  KeyuanChen  阅读(3)  评论(0)    收藏  举报