0、什么是环?

在图论中,(英语:cycle)是一条只有第一个和最后一个顶点重复的非空路径。

在有向图中,一个结点经过两种路线到达另一个结点,未必形成环。

1、拓扑排序

1.1、无向图

使用拓扑排序可以判断一个无向图中是否存在环,具体步骤如下:

  1. 求出图中所有结点的度。
  2. 将所有度 <= 1 的结点入队。(独立结点的度为 0)
  3. 当队列不空时,弹出队首元素,把与队首元素相邻节点的度减一。如果相邻节点的度变为一,则将相邻结点入队。
  4. 循环结束时判断已经访问的结点数是否等于 n。等于 n 说明全部结点都被访问过,无环;反之,则有环。

1.2、有向图

使用拓扑排序判断无向图和有向图中是否存在环的区别在于:

  • 在判断无向图中是否存在环时,是将所有**度 <= 1** 的结点入队;
  • 在判断有向图中是否存在环时,是将所有**入度 = 0** 的结点入队。(感谢 wangweijun@shen 指正!!!)

2、DFS

使用 DFS 可以判断一个无向图和有向中是否存在环。深度优先遍历图,如果在遍历的过程中,发现某个结点有一条边指向已访问过的结点,并且这个已访问过的结点不是上一步访问的结点,则表示存在环。

我们不能仅仅使用一个 bool 数组来表示结点是否访问过。规定每个结点都拥有三种状态,白、灰、黑。开始时所有结点都是白色,当访问过某个结点后,该结点变为灰色,当该结点的所有邻接点都访问完,该节点变为黑色。

那么我们的算法可以表示为:如果在遍历的过程中,发现某个结点有一条边指向灰色节点,并且这个灰色结点不是上一步访问的结点,那么存在环。

#include <iostream>
#include <queue>
#include <vector>
using namespace std;vector<vector<int>> g;
vector<int> color;
int last;
bool hasCycle;bool topo_sort() {int n = g.size();vector<int> degree(n, 0);queue<int> q;for (int i = 0; i < n; i++) {degree[i] = g[i].size();if (degree[i] <= 1) {q.push(i);}}int cnt = 0;while (!q.empty()) {cnt++;int root = q.front();q.pop();for (auto child : g[root]) {degree[child]--;if (degree[child] == 1) {q.push(child);}}}return (cnt != n);
}void dfs(int root) {color[root] = 1;for (auto child : g[root]) {if (color[child] == 1 && child != last) {hasCycle = true;break;}else if (color[child] == 0) {last = root;dfs(child);}}color[root] = 2;
}int main() {int n = 4;g = vector<vector<int>>(n, vector<int>());g[0].push_back(1);g[1].push_back(0);g[1].push_back(2);g[2].push_back(1);g[2].push_back(3);g[3].push_back(2);cout << topo_sort() << endl; //0,无环color = vector<int>(n, 0);last = -1;hasCycle = false;dfs(0);cout << hasCycle << endl;  //0,无环g[0].push_back(3);g[3].push_back(0);cout << topo_sort() << endl; //1,有环color = vector<int>(n, 0);last = -1;hasCycle = false;dfs(0);cout << hasCycle << endl;  //1,有环return 0;
}

3、Union-Find Set

我们可以使用并查集来判断一个图中是否存在环:

对于无向图来说,在遍历边(u-v)时,如果结点 u 和结点 v 的“父亲”相同,那么结点 u 和结点 v 在同一个环中。

对于有向图来说,在遍历边(u->v)时,如果结点 u 的“父亲”是结点 v,那么结点 u 和结点 v 在同一个环中。

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;vector<pair<int, int>> g;
vector<int> father;int findFather(int x) {int a = x;while (x != father[x]) {x = father[x];}while (a != father[a]) {int z = a;a = father[a];father[z] = x;}return x;
}void Union(int a, int b) {int fa = findFather(a);int fb = findFather(b);father[a] = father[b] = min(fa, fb);
}bool isCyclicUnirectedGraph() {for (int i = 0; i < g.size(); i++) {int u = g[i].first;int v = g[i].second;if (father[u] == father[v]) {return true;}Union(u, v);}return false;
}bool isCyclicDirectedGraph() {for (int i = 0; i < g.size(); i++) {int u = g[i].first;int v = g[i].second;if (father[u] == v) {return true;}father[v] = findFather(u);}return false;
}int main() {// Undirected acyclic graph//   0//  / // 1   2g.push_back(make_pair(0, 1));g.push_back(make_pair(0, 2));for (int i = 0; i < 3; i++) {father.push_back(i);}cout << isCyclicUnirectedGraph() << endl;   //0,无环// Undirected cyclic graph//   0//  / // 1———2g.push_back(make_pair(1, 2));vector<int>().swap(father);for (int i = 0; i < 3; i++) {father.push_back(i);}cout << isCyclicUnirectedGraph() << endl;   //1,有环// Directed acyclic graph//   0//  / // v   v// 1——>2vector<pair<int, int>>().swap(g);g.push_back(make_pair(0, 1));g.push_back(make_pair(1, 2));g.push_back(make_pair(0, 2));vector<int>().swap(father);for (int i = 0; i < 3; i++) {father.push_back(i);}cout << isCyclicDirectedGraph() << endl;    //0,无环// Directed cyclic graph//   0//  / ^// v   // 1——>2g.pop_back();g.push_back(make_pair(2, 0));vector<int>().swap(father);for (int i = 0; i < 3; i++) {father.push_back(i);}cout << isCyclicDirectedGraph() << endl;    //1,有环return 0;
}

References

  1. 环 (图论)
  2. 有向无环图
  3. 判断一个图是否有环及相关 LeetCode 题目
  4. 判断有向图是否存在环的 2 种方法(深度遍历,拓扑排序)

aop判断方法是否执行成功_判断图中是否有环的三种方法相关推荐

  1. jquery判断方法是否存在_判断图中是否有环的三种方法

    0.什么是环? 在图论中,环(英语:cycle)是一条只有第一个和最后一个顶点重复的非空路径. 在有向图中,一个结点经过两种路线到达另一个结点,未必形成环. 1.拓扑排序 1.1.无向图 使用拓扑排序 ...

  2. 50兆 svg 文件超过_使用 SVGO 来减小 SVG 文件大小的三种方法

    在这篇文章中我将建议你三种方法,利用 SVGO 让你对 SVG 进行优化,使之适合 Web 使用. 为什么你需要对 SVG 做优化 SVG (全称是 Scalable Vector Graphics) ...

  3. JavaScript 循环中调用异步函数的三种方法,及为什么 forEach 无法工作的分析

    JavaScript 循环中调用异步函数的三种方法,及为什么 forEach 无法工作的分析 业务分析 初版的问题 解决方案 传统的 for 循环 不使用 for 循环的解决方案 分析 forEach ...

  4. Struts2中action接收参数的三种方法及ModelDriven跟Preparable接口结合JAVA反射机制的灵活用法...

    Struts2中action接收参数的三种方法及ModelDriven跟Preparable接口结合JAVA反射机制的灵活用法 www.MyException.Cn   发布于:2012-09-15 ...

  5. mysql在计算机管理中的路径怎么修改_Rstudio中修改工作路径的三种方法

    原文链接: Rstudio中修改工作路径的三种方法_weixin_44370085的博客-CSDN博客_rstudio改变工作目录​blog.csdn.net Rstudio中修改工作路径的三种方法 ...

  6. 计算机启动应用程序的方法,excel的程序_Excel2010中启动应用程序的三种方法

    使用Excel时,需要先启动应用程序,怎么去进行操作启动它?今天,学习啦小编就教大家在Excel2010中启动应用程序的三种方法. Excel2010中启动应用程序的三种步骤如下: 1.开始菜单 在桌 ...

  7. Linux中创建Daemon进程的三种方法

    Linux中创建Daemon进程的三种方法 什么是daemon进程? Unix/Linux中的daemon进程类似于Windows中的后台服务进程,一直在后台运行运行,例如http服务进程nginx, ...

  8. 【转载】取得系统中网卡MAC地址的三种方法

    From:http://blog.csdn.net/zhangting1987/article/details/2732135 网卡地址这个概念有点混淆不清.因为实际上有两个地址,mac地址和物理地址 ...

  9. python csv库,Python 中导入csv数据的三种方法

    Python 中导入csv数据的三种方法,具体内容如下所示: 1.通过标准的Python库导入CSV文件: Python提供了一个标准的类库CSV文件.这个类库中的reader()函数用来导入CSV文 ...

最新文章

  1. js实现UTC时间转为北京时间,时间戳转为时间
  2. Hive之 Hql语法解析
  3. Actor-ES框架:消息发布器与消息存储器
  4. K8s入门教程|这个给集群搞硬装的才是幕后英雄
  5. ISA2004升级到ISA2006需要注意的事项
  6. html让文本框左剧中对齐_Python—Text:功能强大的文本框
  7. 书籍python科学工程介绍 Python for Science and Engineering - 2019
  8. C语言实现字符串转整型
  9. Microsoft Expression Web Designer April CTP Release
  10. kubernetes视频教程笔记 (29)-安全-认证Authentication
  11. mseed读取[2]
  12. 如何升级iOS 16正式版?iOS 16正式版一键刷机教程
  13. 有3种水果(苹果、香蕉、橘子),每位同学只能选1种最爱吃的(共5位同学),要求编一个统计水果票数的程序,先输入水果种类,最后输出各水果得票结果。
  14. Android SO文件保护加固——加密篇(一)
  15. 爬虫之爬取易班推文信息
  16. 4年功能测试月薪9.5K,3个月时间成功进阶自动化,跳槽涨薪6k后我的路还很长...
  17. 怎么使用PS一键抠图?
  18. 金蝶提示系统检测到服务器异常,金蝶提示连接云服务器异常
  19. 20171001-构建之法:现代软件工程-阅读笔记2
  20. 2021届华为提前批面试记录

热门文章

  1. 受限玻尔兹曼机(RBM)与python在Tensorflow的实现
  2. 玩玩机器学习1——ubuntu16.04 64位安装TensorFlow GPU+python3+cuda8.0+cudnn8.0
  3. luogu P2216 [HAOI2007]理想的正方形 递推+ST表
  4. centos 6.2安装mysql_CentOS 6.2下安装MySQL_服务器知识学堂-中关村在线
  5. 局域网中另外一台服务器的内存_局域网共享打印机,但另外一台电脑却看不到,可能是这3个原因...
  6. android 获取phone实例,Android ContentProvider获取手机联系人实例
  7. java for each 原理_Java for each实现机制代码原理解析
  8. 解释下列术语计算机系统结构,计算机系统结构第5章部分习题参考答案
  9. 蓝桥杯小朋友排队java_[蓝桥杯][历届试题]小朋友排队 (C++代码)
  10. cordova 更改app版本_【ios马甲包cps联运】App上架难 马甲包不知道该怎么做?