有向图、无向图找环

简介: 笔记

前言


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


无向图:并查集、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;
    }
};
相关文章
|
5月前
|
算法
Tarjan 求有向图的强连通分量
Tarjan算法是一种用于查找有向图中强连通分量的高效方法。作者分享了自己的笔记,强调了网上解释的不清晰性,并引用了OI Wiki的资源。算法维护每个节点的`dfn`(深度优先搜索顺序)和`low`(能到达的最小DFS序)。通过遍历节点和其邻接节点,更新`low`值,识别环路。当`dfn[x] == low[x]`,表示找到一个强连通分量。代码示例展示了具体的实现细节。
|
6月前
|
存储 算法
有向图和无向图的表示方式(邻接矩阵,邻接表)
有向图和无向图的表示方式(邻接矩阵,邻接表)
197 0
|
6月前
|
人工智能 算法 测试技术
【图论】【拓扑排序】1857. 有向图中最大颜色值
【图论】【拓扑排序】1857. 有向图中最大颜色值
|
C++ 索引
探索图论:无向图、有向图、加权图与无权图
引言 图论是数学中的一个精彩分支,通过图这种数据结构,我们可以更好地理解和分析现实世界中事物之间的关系。在图论中,有四种基本的图类型:无向图、有向图、加权图和无权图。本文将深入探讨这些图的概念,并通过C++代码示例帮助读者更加清晰地理解它们在抽象世界和实际问题中的应用。
739 0
拓扑排序(邻接表实现)
拓扑排序(邻接表实现)
177 0
拓扑排序(邻接表实现)
|
机器学习/深度学习
有向图,无向图的邻接矩阵和邻接表模板
有向图,无向图的邻接矩阵和邻接表模板
192 0
有向图,无向图的邻接矩阵和邻接表模板
|
机器学习/深度学习 存储
图的遍历 无向图深搜 宽搜
图的遍历 无向图深搜 宽搜
图的遍历 无向图深搜 宽搜
|
算法
无向图邻接表(深度优先算法)
无向图邻接表(深度优先算法)
126 0
|
算法
图论——强连通分量:Tarjan算法。
在有向图G中,如果两个定点u,v间存在一条u到v的路径,也存在一条v到u的路径,则称u,v是强连通的。 若有向图G的任意两点都强联通,则称G是一个强联通图。 非强连通图的极大强连通子图称为强连通分量。   这里,极大强连通子图可以理解为一个子图是强连通图,且它的任意子图都不是强联通。
2632 0