可以利用并查集或者带颜色标记的BFS(来自算法导论)判断。

首先介绍第一种,用并查集来判断:

首先初始化所有元素的根为-1,-1代表根节点,接下来对于图中的每一条边(v1,v2)都并入集合,并入的方式为查找v1和v2的根节点,然后让v2的根节点作为v1的根节点,查找根节点的过程为:如果当前的结点根为-1,说明这个结点就是根,直接返回,否则再继续查找结点父亲的根,直到找到祖先结点,这里因为只是判断环路,不需要压缩路径:

  1. int findSet(int x){
  2.  
  3. if(Parent[x] == -1){
  4. return x; // x is root
  5. }
  6. return findSet(Parent[x]);
  7.  
  8. }

在找到v1的根vp1和v2的根vp2以后,首先判断他们是否是同根的,对于一个无环图,某条边并入集合前是不会出现同根的情况的,这是因为这条边中一定有一个结点是新加入集合的(否则这条边就重复了),这个结点的根一定为-1,而另一个已经并入的,会存着根结点的序号(不一定是祖先,因为没有压缩路径),只有图有环的时候才可能两个根相同,因此以vp1是否等于vp2作为图是否有环的依据,一旦发现,即说明有环,直接返回,没有则合并v1、v2,继续进行。

注意:虽然是无向图,但是边只能单向遍历,如果把两个方向的边都遍历,势必有一边出现同根的两结点,一定要注意!!!

具体代码如下:

  1. #include <iostream>
  2. #include <vector>
  3. #include <memory.h>
  4.  
  5. using namespace std;
  6.  
  7. vector<int> Parent;
  8.  
  9. void initSet(){
  10.  
  11. for(int i = 0; i < Parent.size(); i++)
  12. Parent[i] = -1;
  13.  
  14. }
  15.  
  16. int findSet(int x){
  17.  
  18. if(Parent[x] == -1){
  19. return x; // x is root
  20. }
  21. return findSet(Parent[x]);
  22.  
  23. }
  24.  
  25. void UnionSet(int x, int y){
  26.  
  27. int xp = findSet(x);
  28. int yp = findSet(y);
  29. Parent[xp] = yp;
  30.  
  31. }
  32.  
  33. int main(){
  34.  
  35. int N, E;
  36. cin >> N >> E;
  37. vector<vector<int> > edges(N);
  38. Parent.resize(N);
  39. int v1,v2;
  40. for(int i = 0; i < E; i++){
  41. cin >> v1 >> v2;
  42. edges[v1].push_back(v2);
  43. //edges[v2].push_back(v1);
  44. }
  45. // 测试是否有环
  46. initSet();
  47. for(int v = 0; v < edges.size(); v++){
  48. for(int i = 0; i < edges[v].size(); i++){
  49. int w = edges[v][i];
  50. int xp = findSet(v);
  51. int yp = findSet(w);
  52. if(xp == yp){
  53. cout << "未合并前同根,说明有环。" << endl;
  54. return 0;
  55. }
  56. UnionSet(v,w);
  57. }
  58. }
  59. cout << "无环" << endl;
  60. return 0;
  61.  
  62. }

第二种方法,是利用BFS。

我们规定结点有三种颜色,白色、灰色、黑色,在结点没有访问之前,为白色,当结点入队时,结点变灰,出队时变黑。

因为BFS是按层的顺序、从左到右进行遍历的,因此当一个根结点变黑后,也就是它出队以后,接下来要将它的所有未访问过的子结点(邻接点)入队,并且染上灰色,下面我们讨论任一个子结点的颜色。

如果没有环,子结点的颜色只可能是白色,也就是未访问过,如果子结点的颜色为灰色,说明入队过,可能是在根结点变黑(出队)之前就有一个结点有这个子结点作为邻接点,从而进行了第一次访问,这也就是有环的情况,因此,只需要在BFS过程中检测出队结点的邻接点是否有灰色结点即可,有灰色结点可理解得出有环的结论。

具体代码如下:

  1. bool hasCycle(int s){
  2.  
  3. for(int i = 1; i <= N; i++) {
  4. nodesColor[i] = colorWhite;
  5. }
  6.  
  7. queue<int> Q;
  8. Q.push(s);
  9. while(!Q.empty()){
  10.  
  11. int v = Q.front();
  12. Q.pop();
  13. nodesColor[v] = colorGray;
  14. for(int index = 0; index < Graph[v].size(); index++){
  15. int w = Graph[v][index];
  16. if(nodesColor[w] == colorWhite){
  17. Q.push(w);
  18. nodesColor[w] = colorGray;
  19. }else if(nodesColor[w] == colorGray){
  20. return true;
  21. }
  22. }
  23. nodesColor[v] = colorBlack;
  24. }
  25.  
  26. return false;
  27.  
  28. }

判断无向图是否有环路的方法 -并查集 -BFS的更多相关文章

  1. UVA - 10004 Bicoloring(判断二分图——交叉染色法 / 带权并查集)

    d.给定一个图,判断是不是二分图. s.可以交叉染色,就是二分图:否则,不是. 另外,此题中的图是强连通图,即任意两点可达,从而dfs方法从一个点出发就能遍历整个图了. 如果不能保证从一个点出发可以遍 ...

  2. hdu 1272 判断所给的图是不是生成树 (并查集)

    判断所给的图是不是生成树,如果有环就不是,如果没环但连通分量大于1也不是 find函数 用递归写的话 会无限栈溢出 Orz要加上那一串 手动扩栈 Sample Input6 8 5 3 5 2 6 4 ...

  3. UVA 1160 - X-Plosives 即LA3644 并查集判断是否存在环

    X-Plosives A secret service developed a new kind ofexplosive that attain its volatile property only ...

  4. 判断图连通的三种方法——dfs,bfs,并查集

    Description 如果无向图G每对顶点v和w都有从v到w的路径,那么称无向图G是连通的.现在给定一张无向图,判断它是否是连通的. Input 第一行有2个整数n和m(0 < n,m < ...

  5. JS判断字符串长度的5个方法

    这篇文章主要介绍了JS判断字符串长度的5个方法,并且区分中文和英文,需要的朋友可以参考下 目的:计算字符串长度(英文占1个字符,中文汉字占2个字符)   方法一:    代码如下: String.pr ...

  6. js 判断数组包含某值的方法 和 javascript数组扩展indexOf()方法

    var  questionId = []; var anSwerIdValue = []; ////javascript数组扩展indexOf()方法 Array.prototype.indexOf ...

  7. 字符串--java中判断字符串是否为数字的方法的几种方法?

    ava中判断字符串是否为数字的方法: 1.用JAVA自带的函数 public static boolean isNumeric(String str){ for (int i = 0; i < ...

  8. php判断是否为json格式的方法

    php判断是否为json格式的方法. 首先要记住json_encode返回的是字符串, 而json_decode返回的是对象 判断数据不是JSON格式: 复制代码代码如下: function is_n ...

  9. Underscore.js 常用类型判断以及一些有用的工具方法

    1. 常用类型判断以及一些有用的工具方法 underscore.js 中一些 JavaScript 常用类型检查方法,以及一些工具类的判断方法. 首先我们先来谈一谈数组类型的判断.先贴出我自己封装好的 ...

随机推荐

  1. 性能优化之mysql优化——慢查日志的开启方式和存储

    -- MySQL优化 -- mysql 慢查日志的开启方式和存储 -- 1) 查看mysql是否开启慢查询日志 SHOW VARIABLES LIKE 'slow_query_log'; -- 2) ...

  2. Mac Webview OC与JS交互实现

    1.首先,需要定义一个JS可识别的变量(如external)用于OC与JS交互 - (void)webView:(WebView *)sender didClearWindowObject:(WebS ...

  3. Linux(centos7)下安装Docker

    近期公司开始推Docker技术.这个系列的文章都是基于CentOS7系统下进行讲解的. Docker简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器 ...

  4. ERP中的序列管理

    1.序列管理 序列管理主要实现系统用到序列生成规则的配置.主要包含序列配置.序列生产两个功能点. 2.术语说明 序列号:指序列中按步长递进的数字. 序列值:指按规则组合了 "拥有者.序列类型 ...

  5. nodeppt的使用教程

    为什么选择nodeppt 这可能是迄今为止最好的网页版演示库 基于GFM的markdown语法编写 支持html混排,再复杂的demo也可以做! 支持多个皮肤:colors-moon-blue-dar ...

  6. Linux下DIR,dirent,stat等结构体详解

    摘自:http://www.liweifan.com/2012/05/13/linux-system-function-files-operation/ 最近在看Linux下文件操作相关章节,遇到了这 ...

  7. 初始化mysql数据库——Activiti BPM

    package com.initialize; import org.activiti.engine.ProcessEngine; import org.activiti.engine.Process ...

  8. ACM 数塔

    在讲述DP算法的时候,一个经典的例子就是数塔问题,它是这样描述的: 有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?  已经告诉你了,这是个DP的题 ...

  9. 20160210.CCPP体系详解(0020天)

    程序片段(01):01.二级指针.c 内容概要:二级指针 #include <stdio.h> #include <stdlib.h> //01.二级指针: // 1.使用场景 ...

  10. 手动创建第一个OC程序

    手动创建第一个OC程序 创建一个文件夹,文件夹内创建一个a.m的OC源文件,并编辑 之前说过,OC是完全兼容C语言的,那么我们先写个下面的程序尝试一下 #include<stdio.h> ...