判断一个图是否有环 无向图 有向图(转)

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

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. */
时间: 2024-10-11 01:19:16

判断一个图是否有环 无向图 有向图(转)的相关文章

(转)判断一个图是否有环 无向图 有向图

无向图: 法1: 如果存在回路,则必存在一个子图,是一个环路.环路中所有顶点的度>=2. n算法: 第一步:删除所有度<=1的顶点及相关的边,并将另外与这些边相关的其它顶点的度减一. 第二步:将度数变为1的顶点排入队列,并从该队列中取出一个顶点重复步骤一. 如果最后还有未删除顶点,则存在环,否则没有环. n算法分析: 由于有m条边,n个顶点.如果m>=n,则根据图论知识可直接判断存在环路. (证明:如果没有环路,则该图必然是k棵树 k>=1.根据树的性质,边的数目m = n-k.k

【C++】判断一个图是否有环 无向图 有向图(转载)

没有找到原文出处,请参考一下链接: 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的顶点及相关的边,并将另外与这些边相关的其它顶点的度

算法之判断一个图是否有环

在一些经典算法中,经常需要判断一些图是否具有环路,比如拓扑排序,需要在最初判断该图是否有环路,如有有环路,则无法找到最长的一条线,比如dijkstra算法,每找到一条最短的边,都要判断找到的边和现有的树是否已经构成了环路. 因此,在这篇博客,我们重点来说一个判断图是否有环的算法. 首先我们介绍一个对于无向图和有向图通用的算法,先讲算法思路: 1.统计各个图中各个点的入度数(能够到达这个点的点). 2.然后找出入度数为0的点(无向图找入度数为1的点). 3.删除入度数为0的点,将其边也删除. 4.

并查集(判断一个图有几个连通块)

import java.util.Scanner; // 并查集 判断一个图中有几个联通块 public class UnionFind { private int[] father;// private int count;// 分量数量 public UnionFind(int N){ count=N; father=new int[N]; for(int i=0;i<N;i++){ father[i]=i; } } public int count() { return count; }

如何判断一个图中是否存在环路

最近公司的项目中,有个树形结构变图结构的问题.本来我们对项目中实体之间的关系是按树形结构来表示的,也就是说实体之间不会重用,也不会有环.现在我们需要变成图的结构,实体之间可以重用,但不能有环.那么该如何解决这个问题呢? 我们先定义出什么是环: 环定义:从一条边出发,如果能回到当前边则证明有环. 可见,根据定义,上面的图不存在环.因为从任意一条边出发都不可能回到自身.下面给出一个带有环的图. 图中红色箭头的3,4,5构成了一个环路.因为从3.4.5任一点出发,都可以回到起始点.那么我们如何使用代码

poj Transferring Sylla(如何快速的判断一个图是否是3—连通图,求割点,割边)

Transferring Sylla 首先,什么是k连通图?k连通图就是指至少去掉k个点使之不连通的图. 题目: 题目描述的很裸,就是给你一张图要求你判断这图是否是3-连通图. 算法分析: ///////////////////////////////////////////////////////////////////// (网上别人的分析,分析的很好所以直接引用了) 考虑一下不可行的情况,就是存在两点间的路径条数<3情况,那么我们可以去枚举两个点a和b,然后将其和相邻的边删除,然后判断联通

判断一个链表是否有环

思路:如果开始有两个指针指向头结点,一个走的快,一个走的慢,如果有环的话,最终经过若干步,快的指针总会超过慢的指针一圈从而相遇. 如何计算环的长度呢?可以第一次相遇时开始计数,第二次相遇时停止计数. 如何判断环的入口点?碰撞点p到连接点的距离=头指针到连接点的距离,因此,分别从碰撞点.头指针开始走,相遇的那个点就是连接点. 当fast与slow相遇时,show肯定没有走完链表,而fast已经在还里走了n(n>= 1)圈.假设slow走了s步,那么fast走了2s步.fast的步数还等于s走的加上

判断一个图中有无环路的存在

这里要引入两个概念: 1.树边:是一条未被遍历过的边,它指向一个未被访问过的点. 2.反向边:是一条未被遍历过的边,它指向一个被访问过的点. 如果图中有环路的存在,那么环路的最后一个边必然是一条反向边. 我的参考 那么,我们在DFS遍历的过程当中,只需要添加一条语句来判断所有未被检查过的边的指向点是否已被访问过,就可以判断出这个图是否存在环路了. 1 struct Edge { 2 int to, w, next; 3 }; 4 5 struct adjTable { 6 int node[ma

判断一个链表是否有环的几种方法

一.单链表是否有环 思路分析: 单链表有环,是指单链表中某个节点的next指针域指向的是链表中在它之前的某一个节点,这样在链表的尾部形成一个环形结构.判断链表是否有环,有以下几种方法. 1 // 链表的节点结构如下 2 typedef struct node 3 { 4 int data; 5 struct node *next; 6 } NODE; (1)最常用方法:定义两个指针,同时从链表的头节点出发,一个指针一次走一步,另一个指针一次走两步.如果走得快的指针追上了走得慢的指针,那么链表就是