有向图、无向图找环

简介: 笔记

前言


两种图中找环的方法暂且记忆这几种:


无向图:并查集、DFS

有向图:拓扑排序、DFS


无向图


参考例题:找环个数


1. 并查集

通过判断当前要连的边 u , v之间是否已经处在一个集合中,来统计环的个数。

int n, m;
int fa[N];
int find(int x) { return fa[x] = fa[x] == x ? x : find(fa[x]); }
void solve() {
  cin >> n >> m;
  for (int i = 1; i <= n; i++) fa[i] = i;
  int res = 0;
  for (int i = 1; i <= m; i++) {
    int a, b; cin >> a >> b;
    int fx = find(a), fy = find(b);
    if (fx != fy) fa[fx] = fy; 
    else res++;
  }
  cout << res << endl;
}

2. DFS

对于一个环 1-2-3-1 来说,从1开始dfs到2到3到1,发现1已经走过了,计数++。回溯到1,继续dfs到3,发现3也是之前走过的环中的点,计数++。所以最后答案是计数的一半。


简而言之:环始点左右都计数了,但应只取一次。


另一种方法:统计每块联通区域中的点数 V 和边数 E。E >= V 则存在环。

结论:E + P > V 就表示原图有环,否则无环。P(连通分量个数)。

int n, m;
vector<int> e[N]; 
bool st[N]; 
int res = 0;
void dfs(int u, int f) {
  st[u] = true;
  for (auto j: e[u]) {
    if (j == f) continue;
    if (st[j]) { res ++; continue; }
    dfs(j, u);
  }
  return ;
}
void solve() {
  cin >> n >> m;
  for (int i = 1; i <= m; i++) {
    int a, b; cin >> a >> b;
    e[a].pb(b);
    e[b].pb(a);
  }
  memset(st, 0, sizeof st); 
  for (int i = 1; i <= n; i++) {
    if (!st[i]) dfs(i, -1);
  }
  cout << res / 2 << endl; 
}

有向图


参考例题:拓扑排序判环


1. 拓扑排序

记录每个点的入度,入度为 0 00 就加如队列继续bfs。最后判断是否还存在点入度大于 0 00 的,是则存在环。

class Solution {
public:
    bool canFinish(int n, vector<vector<int>>& edge) {
        vector<int> e[n + 1]; 
        vector<int> d(n + 1, 0);
        for (auto c: edge) {
            int a = c[0], b = c[1]; // b -> a
            e[b].push_back(a);
            d[a]++;
        }
        queue<int> q;
        for (int i = 0; i < n; i++) if (d[i] == 0) q.push(i);
        while (q.size()) {
            auto c = q.front(); q.pop(); 
            for (auto j: e[c]) {
                if (--d[j] == 0) q.push(j);
            }
        }
        for (int i = 0; i < n; i++) if (d[i] > 0) return false;
        return true;
    }
};

2. DFS

给状态数组 s t stst 表明含义,如代码注释所示。

class Solution {
public:
    bool canFinish(int n, vector<vector<int>>& edge) {
        vector<int> e[n + 1]; 
        for (auto c: edge) {
            int a = c[0], b = c[1]; // b -> a
            e[b].push_back(a);
        }
        int st[n + 1]; memset(st, 0, sizeof st); 
        bool ok = true; // 无环
        auto dfs = [&](auto &&dfs, int u) -> void {
            st[u] = 1;
            for (auto j: e[u]) {
                if (st[j] == 0) { // 未走过
                    dfs(dfs, j);
                    if (!ok) return;
                } else if (st[j] == 1) { // 自己走过
                    ok = false;
                    return ;
                }
            }
            st[u] = 2; // 不在探索
        };
        for (int i = 0; i < n && ok; i++) if (!st[i]) dfs(dfs, i);
        return ok;
    }
};
相关文章
|
7月前
|
存储 算法
有向图和无向图的表示方式(邻接矩阵,邻接表)
有向图和无向图的表示方式(邻接矩阵,邻接表)
315 0
|
7月前
|
人工智能 算法 测试技术
【图论】【拓扑排序】1857. 有向图中最大颜色值
【图论】【拓扑排序】1857. 有向图中最大颜色值
|
机器学习/深度学习 算法
无向图的算法:Kruskal算法与Prim算法生成最小生成树
无向图的算法:Kruskal算法与Prim算法生成最小生成树
197 0
|
机器学习/深度学习
有向图,无向图的邻接矩阵和邻接表模板
有向图,无向图的邻接矩阵和邻接表模板
196 0
有向图,无向图的邻接矩阵和邻接表模板
邻接矩阵
数据结构中无向图邻接矩阵的存储
邻接矩阵
|
机器学习/深度学习 存储
图的遍历 无向图深搜 宽搜
图的遍历 无向图深搜 宽搜
图的遍历 无向图深搜 宽搜
|
算法
狄克斯特拉(Dijkstra)算法求一个顶点到其余各个顶点的最短路径
狄克斯特拉(Dijkstra)算法求一个顶点到其余各个顶点的最短路径
131 0
|
算法
无向图邻接表(深度优先算法)
无向图邻接表(深度优先算法)
136 0