有向图、无向图找环

简介: 笔记

前言


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


无向图:并查集、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;
    }
};
相关文章
|
6月前
|
监控 数据可视化 数据挖掘
数据可视化软件推荐:10款释放数据价值的高效工具指南
在数字经济时代,数据可视化已成为企业决策的核心工具。本指南深度盘点10款主流软件,涵盖瓴羊Quick BI、Tableau、Power BI等,从智能分析、生态协同到成本适配,全面解析各工具优势与适用场景,助力企业按需选型,让数据真正赋能业务决策。其中,瓴羊 Quick BI(阿里云旗下BI产品) 凭借连续6年中国唯一入选Gartner魔力象限的成熟度、“智能小Q”AI模块的实战价值及阿里云生态协同优势,成为多场景分析的重要选择,同时其他工具也各有侧重,可满足不同生态背景与业务需求的企业。
|
运维 监控 算法
聊一聊Sentinel背后的原理
本文介绍了Sentinel的核心原理,包括流量控制、熔断降级、系统负载保护、实时监控和统计、与多种微服务框架的集成能力以及扩展性,强调了Sentinel在保障分布式系统稳定性方面的重要性。
1213 0
|
编译器 C++
fmt文本格式库的源码下载编译(Win10+VS2022)
fmt文本格式库的源码下载编译(Win10+VS2022)
1122 0
|
SQL 缓存 分布式计算
flink1.18 SqlGateway 的使用和原理分析
# 了解flink1.18 sqlGateway 的安装和使用步骤 # 启动sqlgateway 流程,了解核心的结构 # sql提交流程,了解sql 的流转逻辑 # select 查询的ResultSet的对接流程,了解数据的返回和获取逻辑
|
算法 NoSQL 关系型数据库
9种 分布式ID生成方式
9种 分布式ID生成方式
1560 0
|
数据处理 Python
【Python】已解决:SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFram
【Python】已解决:SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFram
3071 1
|
移动开发 Dart 前端开发
深度分析:React Native、Flutter、UniApp、Taro、Vue的差异
深度分析:React Native、Flutter、UniApp、Taro、Vue的差异
1500 6
|
存储 Python
链表中删除节点
链表中删除节点
|
Nacos
分布式理论:CAP理论 BASE理论
分布式理论:CAP理论 BASE理论
296 2
|
安全 Unix Linux
第一章 操作系统概述
第一章 操作系统概述
1221 0