没有找到原文出处,请参考一下链接:

http://www.cnblogs.com/hiside/archive/2010/12/01/1893878.html

http://topic.csdn.net/u/20071023/11/3edb81fc-37b2-4506-906e-44dc0fc521f2.html

一、无向图:

方法1:

  • 如果存在回路,则必存在一个子图,是一个环路。环路中所有顶点的度>=2。
  • n算法:

第一步:删除所有度<=1的顶点及相关的边,并将另外与这些边相关的其它顶点的度减一。

第二步:将度数变为1的顶点排入队列,并从该队列中取出一个顶点重复步骤一。

如果最后还有未删除顶点,则存在环,否则没有环。

  • n算法分析:

由于有m条边,n个顶点。

i)如果m>=n,则根据图论知识可直接判断存在环路。(证明:如果没有环路,则该图必然是k棵树 k>=1。根据树的性质,边的数目m = n-k。k>=1,所以:m<n)

ii)如果m<n 则按照上面的算法每删除一个度为0的顶点操作一次(最多n次),或每删除一个度为1的顶点(同时删一条边)操作一次(最多m次)。这两种操作的总数不会超过m+n。由于m<n,所以算法复杂度为O(n)。

  • 注:

该方法,算法复杂度不止O(V),首先初始时刻统计所有顶点的度的时候,复杂度为(V + E),即使在后来的循环中E>=V,这样算法的复杂度也只能为O(V + E)。其次,在每次循环时,删除度为1的顶点,那么就必须将与这个顶点相连的点的度减一,并且执行delete node from list[list[node]],这里查找的复杂度为list[list[node]]的长度,只有这样才能保证当degree[i]=1时,list[i]里面只有一个点。这样最差的复杂度就为O(EV)了。

方法2:

DFS搜索图,图中的边只可能是树边或反向边,一旦发现反向边,则表明存在环。该算法的复杂度为O(V)。

方法3:

摘自:http://blog.csdn.net/lzrzhao/archive/2008/03/13/2175787.aspx

PS:此方法于2011-6-12补充

假定:图顶点个数为M,边条数为E

遍历一遍,判断图分为几部分(假定为P部分,即图有 P 个连通分量)
对于每一个连通分量,如果无环则只能是树,即:边数=结点数-1
只要有一个满足      边数   >   结点数-1
原图就有环
将P个连通分量的不等式相加,就得到:
P1:E1=M1-1
P2:E2=M2-1
...
PN:EN>MN-1
    所有边数(E)   >   所有结点数(M) - 连通分量个数(P)
即:  E + P > M  所以只要判断结果  E  + P > M 就表示原图有环,否则无环.

实例代码如下:

  1. #include<iostream>
  2. #include<malloc.h>
  3. using namespace std;
  4. #define maxNum 100 //定义邻接举证的最大定点数
  5. int visited[maxNum];//通过visited数组来标记这个顶点是否被访问过,0表示未被访问,1表示被访问
  6. int DFS_Count;//连通部件个数,用于测试无向图是否连通,DFS_Count=1表示只有一个连通部件,所以整个无向图是连通的
  7. int pre[maxNum];
  8. int post[maxNum];
  9. int point;//pre和post的值
  10. //图的邻接矩阵表示结构
  11. typedef struct
  12. {
  13. char v[maxNum];//图的顶点信息
  14. int e[maxNum][maxNum];//图的顶点信息
  15. int vNum;//顶点个数
  16. int eNum;//边的个数
  17. }graph;
  18. void createGraph(graph *g);//创建图g
  19. void DFS(graph *g);//深度优先遍历图g
  20. void dfs(graph *g,int i);//从顶点i开始深度优先遍历与其相邻的点
  21. void dfs(graph *g,int i)
  22. {
  23. //cout<<"顶点"<<g->v[i]<<"已经被访问"<<endl;
  24. cout<<"顶点"<<i<<"已经被访问"<<endl;
  25. visited[i]=1;//标记顶点i被访问
  26. pre[i]=++point;
  27. for(int j=1;j<=g->vNum;j++)
  28. {
  29. if(g->e[i][j]!=0&&visited[j]==0)
  30. dfs(g,j);
  31. }
  32. post[i]=++point;
  33. }
  34. void DFS(graph *g)
  35. {
  36. int i;
  37. //初始化visited数组,表示一开始所有顶点都未被访问过
  38. for(i=1;i<=g->vNum;i++)
  39. {
  40. visited[i]=0;
  41. pre[i]=0;
  42. post[i]=0;
  43. }
  44. //初始化pre和post
  45. point=0;
  46. //初始化连通部件数为0
  47. DFS_Count=0;
  48. //深度优先搜索
  49. for(i=1;i<=g->vNum;i++)
  50. {
  51. if(visited[i]==0)//如果这个顶点为被访问过,则从i顶点出发进行深度优先遍历
  52. {
  53. DFS_Count++;//统计调用void dfs(graph *g,int i);的次数
  54. dfs(g,i);
  55. }
  56. }
  57. }
  58. void createGraph(graph *g)//创建图g
  59. {
  60. cout<<"正在创建无向图..."<<endl;
  61. cout<<"请输入顶点个数vNum:";
  62. cin>>g->vNum;
  63. cout<<"请输入边的个数eNum:";
  64. cin>>g->eNum;
  65. int i,j;
  66. //输入顶点信息
  67. //cout<<"请输入顶点信息:"<<endl;
  68. //for(i=0;i<g->vNum;i++)
  69. //  cin>>g->v[i];
  70. //初始画图g
  71. for(i=1;i<=g->vNum;i++)
  72. for(j=1;j<=g->vNum;j++)
  73. g->e[i][j]=0;
  74. //输入边的情况
  75. cout<<"请输入边的头和尾"<<endl;
  76. for(int k=0;k<g->eNum;k++)
  77. {
  78. cin>>i>>j;
  79. g->e[i][j]=1;
  80. g->e[j][i]=1;//无向图对称
  81. }
  82. }
  83. int main()
  84. {
  85. graph *g;
  86. g=(graph*)malloc(sizeof(graph));
  87. createGraph(g);//创建图g
  88. DFS(g);//深度优先遍历
  89. //连通部件数,用于判断是否连通图
  90. cout<<"连通部件数量:";
  91. cout<<DFS_Count<<endl;
  92. if(DFS_Count==1)
  93. cout<<"图g是连通图"<<endl;
  94. else if(DFS_Count>1)
  95. cout<<"图g不是连通图"<<endl;
  96. //各顶点的pre和post值
  97. for(int i=1;i<=g->vNum;i++)
  98. cout<<"顶点"<<i<<"的pre和post分别为:"<<pre[i]<<" "<<post[i]<<endl;
  99. //cout<<endl;
  100. //判断无向图中是否有环
  101. if(g->eNum+DFS_Count>g->vNum)
  102. cout<<"图g中存在环"<<endl;
  103. else
  104. cout<<"图g中不存在环"<<endl;
  105. int k;
  106. cin>>k;
  107. return 0;
  108. }
  109. /*
  110. 输入:
  111. 正在创建无向图...
  112. 请输入顶点个数vNum:10
  113. 请输入边的个数eNum:9
  114. 请输入边的头和尾
  115. 1 2
  116. 1 4
  117. 2 5
  118. 2 6
  119. 4 7
  120. 5 9
  121. 6 3
  122. 7 8
  123. 9 10
  124. */

注意:有向图不能使用此方法。比如1->2,1-3,2->3,4->5,如果使用上述方法会判定为含有还,但并非如此。

有向图:

主要有深度优先和拓扑排序2中方法

1、拓扑排序,如果能够用拓扑排序完成对图中所有节点的排序的话,就说明这个图中没有环,而如果不能完成,则说明有环。

2、可以用Strongly Connected Components来做,我们可以回忆一下强连通子图的概念,就是说对于一个图的某个子图,该子图中的任意u->v,必有v->u,则这是一个强连通子图。这个限定正好是环的概念。所以我想,通过寻找图的强连通子图的方法应该可以找出一个图中到底有没有环、有几个环。

3、就是用一个改进的DFS

刚看到这个问题的时候,我想单纯用DFS就可以解决问题了。但细想一下,是不能够的。如果题目给出的是一个无向图,那么OK,DFS是可以解决的。但无向图得不出正确结果的。比如:A->B,A->C->B,我们用DFS来处理这个图,我们会得出它有环,但其实没有。

我们可以对DFS稍加变化,来解决这个问题。解决的方法如下:

图中的一个节点,根据其C[N]的值,有三种状态:

0,此节点没有被访问过

-1,被访问过至少1次,其后代节点正在被访问中

1,其后代节点都被访问过。

按照这样的假设,当按照DFS进行搜索时,碰到一个节点时有三种可能:

1、如果C[V]=0,这是一个新的节点,不做处理

2、如果C[V]=-1,说明是在访问该节点的后代的过程中访问到该节点本身,则图中有环。

3、如果C[V]=1,类似于2的推导,没有环。    在程序中加上一些特殊的处理,即可以找出图中有几个环,并记录每个环的路径

PS:此代码实现于2011-6-13补充

改进DFS算法代码示例(判断是否是一个有向无环图)

  1. #include<iostream>
  2. #include<malloc.h>
  3. using namespace std;
  4. #define maxNum 100 //定义邻接举证的最大定点数
  5. int pre[maxNum];
  6. int post[maxNum];
  7. int point=0;//pre和post的值
  8. bool is_DAG=true;//标识位,表示有向无环图
  9. /*
  10. 顶点颜色表 color[u]
  11.    0 白色,未被访问过的节点标白色
  12.    -1 灰色,已经被访问过一次的节点标灰色
  13.    1 黑色,当该节点的所有后代都被访问过标黑色
  14. 反向边:
  15.    如果第一次访问(u,v)时v为灰色,则(u,v)为反向边。在对图的深度优先搜索中没有发现
  16.    反向边,则该图没有回路
  17. 程序判断依据:
  18. 仍然是按图的节点深度遍历,访问到V时,V若被访问过,那么有2种状态:
  19. color[u]=-1,程序跳出,存在环
  20. color[u]=1,程序继续,这不是环
  21. 时间复杂度:O(n+e)
  22. */
  23. int color[maxNum];//顶点颜色表 color[u]
  24. //图的邻接矩阵表示结构
  25. typedef struct
  26. {
  27. char v[maxNum];//图的顶点信息
  28. int e[maxNum][maxNum];//图的顶点信息
  29. int vNum;//顶点个数
  30. int eNum;//边的个数
  31. }graph;
  32. void createGraph(graph *g);//创建图g
  33. void DFS(graph *g);//深度优先遍历图g
  34. void dfs(graph *g,int i);//从顶点i开始深度优先遍历与其相邻的点
  35. void dfs(graph *g,int i)
  36. {
  37. //cout<<"顶点"<<g->v[i]<<"已经被访问"<<endl;
  38. cout<<"顶点"<<i<<"已经被访问"<<endl;
  39. color[i]=-1;
  40. pre[i]=++point;
  41. for(int j=1;j<=g->vNum;j++)
  42. {
  43. if(g->e[i][j]!=0)
  44. {
  45. if(color[j]==-1)//探索到回边,存在环
  46. {
  47. is_DAG=false;//不是有向无环图
  48. }
  49. else if(color[j]==0)
  50. dfs(g,j);
  51. }
  52. }
  53. post[i]=++point;
  54. color[i]=1;//表示i的后裔节点都被访问过
  55. }
  56. void DFS(graph *g)
  57. {
  58. int i;
  59. //初始化color数组,表示一开始所有顶点都未被访问过,//初始化pre和post
  60. for(i=1;i<=g->vNum;i++)
  61. {
  62. color[i]=0;
  63. pre[i]=0;
  64. post[i]=0;
  65. }
  66. //深度优先搜索
  67. for(i=1;i<=g->vNum;i++)
  68. {
  69. if(color[i]==0)//如果这个顶点为被访问过,则从i顶点出发进行深度优先遍历
  70. {
  71. dfs(g,i);
  72. }
  73. }
  74. }
  75. void createGraph(graph *g)//创建图g
  76. {
  77. cout<<"正在创建无向图..."<<endl;
  78. cout<<"请输入顶点个数vNum:";
  79. cin>>g->vNum;
  80. cout<<"请输入边的个数eNum:";
  81. cin>>g->eNum;
  82. int i,j;
  83. //初始画图g
  84. for(i=1;i<=g->vNum;i++)
  85. for(j=1;j<=g->vNum;j++)
  86. g->e[i][j]=0;
  87. //输入边的情况
  88. cout<<"请输入边的头和尾"<<endl;
  89. for(int k=1;k<=g->eNum;k++)
  90. {
  91. cin>>i>>j;
  92. g->e[i][j]=1;
  93. }
  94. }
  95. int main()
  96. {
  97. graph *g;
  98. g=(graph*)malloc(sizeof(graph));
  99. createGraph(g);//创建图g
  100. DFS(g);//深度优先遍历
  101. //各顶点的pre和post值
  102. for(int i=1;i<=g->vNum;i++)
  103. cout<<"顶点"<<i<<"的pre和post分别为:"<<pre[i]<<" "<<post[i]<<endl;
  104. //判断是否是有向无环图
  105. if(is_DAG)
  106. cout<<"图g是有向无环图,没有环"<<endl;
  107. else
  108. cout<<"图g不是有向无环图,存在环"<<endl;
  109. int k;
  110. cin>>k;
  111. return 0;
  112. }
  113. /*
  114. 输入1:
  115. 正在创建无向图...
  116. 请输入顶点个数vNum:3
  117. 请输入边的个数eNum:3
  118. 请输入边的头和尾
  119. 1 2
  120. 1 3
  121. 3 2
  122. 输入2:
  123. 正在创建无向图...
  124. 请输入顶点个数vNum:4
  125. 请输入边的个数eNum:4
  126. 请输入边的头和尾
  127. 1 2
  128. 2 3
  129. 3 4
  130. 4 2
  131. */

【C++】判断一个图是否有环 无向图 有向图(转载)的更多相关文章

  1. poj2762 判断一个图中任意两点是否存在可达路径 也可看成DAG的最小覆盖点是否为1

      Going from u to v or from v to u? Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 179 ...

  2. 图论期末大作业编程题(如何判断一个4连通4正则图为无爪、无K4图)

    博士期间估计这可能是唯一一个要编程的作业,搞了半天弄出这个东西,放这里为以后用到的时候查找方便. 说来也是可笑,读博士期间发现大家对上课也都没什么兴趣,老师也是那么回事,都说博士期间学的课程是要有助于 ...

  3. Geeks - Detect Cycle in a Directed Graph 推断图是否有环

    Detect Cycle in a Directed Graph 推断一个图是否有环,有环图例如以下: 这里唯一注意的就是,这是个有向图, 边组成一个环,不一定成环,由于方向能够不一致. 这里就是添加 ...

  4. 判断单链表是否有环,并找出环的入口python

    1.如何判断一个链表是否有环? 2.如果链表为存在环,如果找到环的入口点? 1.限制与要求 不允许修改链表结构. 时间复杂度O(n),空间复杂度O(1). 2.思考 2.1判断是否有环 如果链表有环, ...

  5. DFS判断图是否有环

      利用_DFS_来判断无向图是否存在环的条件思路,我看一次_DFS_是否能访问到之前访问到的节点,如果能够访问到,就说明图存在环,那么关键问题就是判断是一次DFS?,追根到_DFS_算法的实现细节, ...

  6. HDU4514(非连通图的环判断与图中最长链)

    题目:设计风景线 题意:给定一个无向图,图可能是非连通的,如果图中存在环,就输出YES,否则就输出图中最长链的长度. 分析:首先我们得考虑这是一个无向图,而且有可能是非连通的,那么就不能直接像求树那样 ...

  7. ZOJ 1015 Fishing Net(判断弦图)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=15 题意:给定一个图.判断是不是弦图? 思路:(1)神马是弦图?对于一 ...

  8. 图结构练习——判断给定图是否存在合法拓扑序列(dfs算法(第一个代码),邻接矩阵(前两个代码),邻接表(第三个代码))

    sdut 2140 图结构练习——判断给定图是否存在合法拓扑序列 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述  给定一个有向图 ...

  9. HDU 3594 Cactus (强连通分量 + 一个边只能在一个环里)

    题意:判断题目中给出的图是否符合两个条件.1 这图只有一个强连通分量 2 一条边只能出现在一个环里. 思路:条件1的满足只需要tarjan算法正常求强连通分量即可,关键是第二个条件,我们把对边的判断转 ...

随机推荐

  1. PAT甲级——A1010 Radix

    Given a pair of positive integers, for example, 6 and 110, can this equation 6 = 110 be true? The an ...

  2. rdf(资源描述框架)

    资源描述框架(Resource Description Framework),一种用于描述Web资源的标记语言.RDF是一个处理元数据的XML(标准通用标记语言的子集)应用,所谓元数据,就是“描述数据 ...

  3. 注册.NET Framework

    C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe /i

  4. PHP的cURL扩展库使用详解

    在还没有接触curl的时候,相信大家在获取网页内容的时,使用得最多的一个函数就是:file_get_contents(),但是它的可控制性不够灵活,无法处理错误情况,对于各种复杂情况的采集更是显得有点 ...

  5. Hadoop Serialization -- hadoop序列化详解 (2)【Text,BytesWritable,NullWritable】

    回顾: 回顾序列化,其实原书的结构很清晰,我截图给出书中的章节结构: 序列化最主要的,最底层的是实现writable接口,wiritable规定读和写的游戏规则 (void write(DataOut ...

  6. 用惯了jquery, 想用angularjs 还真不好理解

    jquery 比较直白,什么都是操作dom 节点. angularjs 就好比 thinkphp, ci 等框架,有自己约定的格式和方式.需要遵循它的规则,研究中... 比如说我,用了很长事件的jqu ...

  7. Redis源码解析:20sentinel(一)初始化、建链

    sentinel(哨兵)是redis的高可用解决方案.由一个或多个sentinel实例组成的分布式系统,可以监控任意多个主节点,以及它们属下的所有从节点.当某个主节点下线时,sentinel可以将下线 ...

  8. django模块安装环境变量

    django 模块 一 安装: 方法一: (在 JetBrains PyCharm 2017.2 软件的) 设置 (里找到) 项目:python +(添加) (搜索) django Install p ...

  9. Web前端开发的就业前景怎么样,薪资待遇如何

    信息技术的迅速发展,使IT技术者们赶上了一个百年难遇的好机会,尤其是国家出台了“互联网+”的政策后,更是催生了IT行业的就业空间,使其呈现爆发性增长. 如今,微信逐渐成为了大家主要的交流工具,随着各种 ...

  10. Hibernate中的Query

    //1:创建query对象,方法里面写hql语句 Query query = session.createQuery("from User"); //2:利用query对象中的方法 ...