当前位置:文档之家› (完整word版)判断一个图是否有环无向图有向图讲解

(完整word版)判断一个图是否有环无向图有向图讲解

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

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

第二步:将度数变为1的顶点排入队列,并从该队列中取出一个顶点重复步骤一。 如果最后还有未删除顶点,则存在环,否则没有环。  n算法分析: 由于有m条边,n个顶点。 i)如果m>=n,则根据图论知识可直接判断存在环路。(证明:如果没有环路,则该图必然是k棵树 k>=1。根据树的性质,边的数目m = n-k。k>=1,所以:m

ii)如果m每删除一个度为1的顶点(同时删一条边)操作一次(最多m次)。这两种操作的总数不会超过m+n。由于m 注:

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

注意:有向图不能使用此方法。比如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次,其后代节点正在被访问中

相关主题