当前位置:   article > 正文

拓扑排序:判断有向图是否有环(超级详细剖析!!)+207. Course Schedule实例_拓扑排序可以判断有向图是否有环

拓扑排序可以判断有向图是否有环

本文先给定义,接着以举例的形式讲解算法原理,最后使用python实践

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

定义:

直接copy一下 百科上面的吧:

对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。

使用的算法也很简单步骤大概就是:这里使用bfs(dfs遍历也可以)

一:先计算所有节点的入度

二:将所有入度为0的点进入一个队列

三:然后出队,得到一个元素

四:将该元素指向的所有元素的入度减1

五:如果减后其入度为0则将该元素入队列

六:继续出队重复步骤三,直到队列为空

还是举个例子吧:

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

假设队列为queue

依据图可以得到每个节点的入度:

{A:0,B:1,C:1,D:2,E:1,F:1,G:1,H:1}

一开始只有入度为0的节点入队即A节点入队

queue= [A]

依次遍历其所有指向元素:

首先是B,将其入度减为0,然后其入度为1-1=0,入度为0入队

queue = [B],然后是E,入度减1为0,入度为0入队queue = [B,E]

此时{A:0,B:0,C:1,D:2,E:0,F:1,G:1,H:1}

为了可视化,上面的入度减一的过程其实可以看成擦除边的过程,即上面这一个循环结束后,拓扑图就变成:

好了,B出队列,其指向的C的入度减一,C的入度为0,进入队列,此时:

queue = [E,C]

{A:0,B:0,C:0,D:2,E:0,F:1,G:1,H:1}

拓扑图变为:

E出队,F入度减一入队

queue = [C,F]

{A:0,B:0,C:0,D:2,E:0,F:0,G:1,H:1}

C出队,首先遍历D,D入度减一变为1,不为0,不如队列,

              接着遍历H,H入度减一变为0,入队列

queue = [F,H]

{A:0,B:0,C:0,D:1,E:0,F:0,G:1,H:0}

F出队,遍历D,D的入度减一变为0,入队列

queue = [H,D]

{A:0,B:0,C:0,D:0,E:0,F:0,G:1,H:0}

H出队列,其没有所指向的元素,直接跳过,此时:

queue = [D]

{A:0,B:0,C:0,D:0,E:0,F:0,G:1,H:0}

D出队列,遍历到G,其入度减一为0,入队

queue = [G]

{A:0,B:0,C:0,D:0,E:0,F:0,G:0,H:0}

G出队列,其没有所指的元素,直接跳过

 

再次出队发现队列为空结束。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

上述出入队列的顺序就是拓扑排序的顺序即ABECHFDG

还没完呢!!!!!!!!!!!!!!!!!

其实它的更多用途是用来判断有向图是否存在环当出队的个数等于图节点个数的时候是无环图,相反便是有环图

比如上面出队的个数是8个(ABECHFDG),图上节点也是8个(ABCDEFGH)所以上图就是无环图,为了对比,下面我们再举一个有环图看一下:很明显红圈的部分就是一个环

我们按上面的算法走一下,这里我就不再详细叙述了,直接给出每一步的结果

开始的时候

queue=[A]

{A:0,B:2,C:1,D:1,E:1}

A出队操作后:

queue=[D]

{A:0,B:1,C:1,D:0,E:1}

D出队列,发现没有所指元素,直接跳过

queue=[]

{A:0,B:1,C:1,D:0,E:1}

此时发现队列为空,没有元素可出,结束

 

最后发现出队的元素个数是2(A,D),而图上的个数是5个(ABCDE),两者不相同证明有环,同时注意有环的时候是没有拓扑排序的即没有拓扑排序的

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

这里给一下判断有向图是否有环的模板吧

  1. queue<int>q;
  2. for(int i=0;i<n;i++) //n 节点的总数
  3. if(in[i]==0) q.push(i); //将入度为0的点入队列
  4. vector<int>ans; //ans 为拓扑序列
  5. while(!q.empty())
  6. {
  7. int p=q.top(); q.pop(); // 选一个入度为0的点,出队列
  8. ans.push_back(p);
  9. for(int i=0;i<edge[p].size();i++)
  10. {
  11. int y=edge[p][i];
  12. in[y]--;
  13. if(in[y]==0)
  14. q.push(y);
  15. }
  16. }
  17. if(ans.size()==n)
  18. {
  19. for(int i=0;i<ans.size();i++)
  20. printf( "%d ",ans[i] );
  21. printf("\n");
  22. }
  23. else printf("No Answer!\n"); // ans 中的长度与n不相等,就说明无拓扑序列

来至于:https://blog.csdn.net/qq_41713256/article/details/80805338

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

上面部分是理论部分,下面看一下具体实践

很多题目其实可以转化为判断一个有向图是否有环的问题,比如leetcode上面的207. Course Schedule

根据上面的理论,解决该问题就轻而易举啦,代码如下(python)

  1. class Solution(object):
  2. def canFinish(self, numCourses, prerequisites):
  3. """
  4. :type numCourses: int
  5. :type prerequisites: List[List[int]]
  6. :rtype: bool
  7. """
  8. queue = []
  9. ins = [0 for i in range(numCourses)]
  10. for prerequisite in prerequisites:
  11. ins[prerequisite[0]]+=1
  12. for i in range(numCourses):
  13. if ins[i]==0:
  14. queue.append(i)
  15. count = len(queue)
  16. while queue:
  17. n = queue.pop(0)
  18. for prerequisite in prerequisites:
  19. if n== prerequisite[1]:
  20. ins[prerequisite[0]]-=1
  21. if ins[prerequisite[0]]==0:
  22. queue.append(prerequisite[0])
  23. count+=1
  24. return numCourses==count

同理下面这道题也很简单:210. Course Schedule II

它要求在判断有无环的同时,同时要求如果是无环,要返回我们上面说的拓扑序列,否者返回空

这里其实只需要再增加一个列表比如result,用其记录每次出队列的元素,如果最后判断是无环的话,就返回该列表,否者返回空列表即可

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/64607
推荐阅读
相关标签
  

闽ICP备14008679号