有向图、无向图找环

简介: 笔记

前言


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


无向图:并查集、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;
    }
};
相关文章
|
3月前
|
算法 BI 开发工具
Myers 差异算法:高效比较序列的利器(文本对比)
Myers差异算法是一种高效比较文本或数据序列差异的算法,通过构建“编辑图”并寻找最短路径,快速找出两个序列的最小编辑操作(插入、删除、匹配)。它在代码版本管理、文档对比等领域广泛应用,具有高效、精准的特点,尤其适用于相似度高的序列,极大提升了差异计算效率。
|
并行计算 Linux Go
export GOMP_CPU_AFFINITY=0-(((npro
export GOMP_CPU_AFFINITY=0-(((nproc --all) - 1 )) 是一条 Linux 命令,用于设置 GOMP(Go 语言的 OpenMP 支持库)使用的 CPU 亲和性。
583 1
|
运维 监控 算法
聊一聊Sentinel背后的原理
本文介绍了Sentinel的核心原理,包括流量控制、熔断降级、系统负载保护、实时监控和统计、与多种微服务框架的集成能力以及扩展性,强调了Sentinel在保障分布式系统稳定性方面的重要性。
851 0
|
10月前
|
算法 Serverless
基于魏格纳函数和焦散线方法的自加速光束matlab模拟与仿真
本项目基于魏格纳函数和焦散线方法,使用MATLAB 2022A模拟自加速光束。通过魏格纳函数法生成多种自加速光束,并设计相应方法,展示仿真结果。核心程序包括相位和幅度的计算、光场分布及拟合分析,实现对光束传播特性的精确控制。应用领域涵盖光学成像、光操控和光束聚焦等。 关键步骤: 1. 利用魏格纳函数计算光场分布。 2. 模拟并展示自加速光束的相位和幅度图像。 3. 通过拟合分析,验证光束加速特性。 该算法原理基于魏格纳函数描述光场分布,结合数值模拟技术,实现对光束形状和传播特性的精确控制。通过调整光束相位分布,可改变其传播特性,如聚焦或加速。
261 20
|
机器学习/深度学习 算法 安全
随机性、熵与随机数生成器:解析伪随机数生成器(PRNG)和真随机数生成器(TRNG)
随机性在密码学、仿真和机器学习等领域中至关重要,本文探讨了随机性、熵的概念以及伪随机数生成器(PRNG)和真随机数生成器(TRNG)的原理和应用。PRNG通过算法生成看似随机的序列,适用于高效需求;TRNG利用物理过程生成真正随机数,适用于高安全需求。文章还讨论了两者的协同应用及其面临的挑战。
695 5
随机性、熵与随机数生成器:解析伪随机数生成器(PRNG)和真随机数生成器(TRNG)
|
12月前
|
存储 缓存 算法
【C++】BitSet和Bloom_Filter
位图(Bitmap)和布隆过滤器(Bloom Filter)是两种高效的数据结构。位图使用每一位二进制数表示数据项的存在状态,适用于精确判断元素存在性,广泛应用于图形图像处理、数据压缩、数据库索引等领域。布隆过滤器通过多个哈希函数将元素映射到位数组,用于快速判断元素是否可能属于集合,特别适合处理大规模数据,尽管存在误判率,但在网页缓存、网络数据包过滤等场景中表现出色。两者在空间效率、查询速度及误判率方面各有优势,适用于不同的应用场景。
188 4
|
前端开发 JavaScript API
node事件循环中事件执行顺序
本文详细介绍了Node.js环境下的事件循环机制,包括其六个主要阶段:timers、I/O callbacks、idle, prepare、poll、check和close callbacks。文章通过具体代码示例解释了`setTimeout`、`setImmediate`和`process.nextTick`在事件循环中的执行顺序和区别。还探讨了在不同情况下(如I/O操作中)这些函数的执行顺序如何受到影响。最后,通过一个综合例子,展示了实际编码中事件循环的执行顺序。
231 1
node事件循环中事件执行顺序
|
算法 NoSQL 关系型数据库
9种 分布式ID生成方式
9种 分布式ID生成方式
1167 0
|
机器学习/深度学习 数据采集 算法
如何在一夜之间成为模型微调大师?——从零开始的深度学习修炼之旅,让你的算法功力飙升!
【10月更文挑战第5天】在机器学习领域,预训练模型具有强大的泛化能力,但直接使用可能效果不佳,尤其在特定任务上。此时,模型微调显得尤为重要。本文通过图像分类任务,详细介绍如何利用PyTorch对ResNet-50模型进行微调,包括环境搭建、数据预处理、模型加载与训练等步骤,并提供完整Python代码。通过调整超参数和采用早停策略等技巧,可进一步优化模型性能。适合初学者快速上手模型微调。
698 8
|
存储 算法 C++
高精度算法(加、减、乘、除,使用c++实现)
高精度算法(加、减、乘、除,使用c++实现)
2743 0
高精度算法(加、减、乘、除,使用c++实现)