赞
踩
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
定义:
直接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),两者不相同证明有环,同时注意有环的时候是没有拓扑排序的即没有拓扑排序的
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
这里给一下判断有向图是否有环的模板吧
- queue<int>q;
- for(int i=0;i<n;i++) //n 节点的总数
- if(in[i]==0) q.push(i); //将入度为0的点入队列
- vector<int>ans; //ans 为拓扑序列
- while(!q.empty())
- {
- int p=q.top(); q.pop(); // 选一个入度为0的点,出队列
- ans.push_back(p);
- for(int i=0;i<edge[p].size();i++)
- {
- int y=edge[p][i];
- in[y]--;
- if(in[y]==0)
- q.push(y);
- }
- }
- if(ans.size()==n)
- {
- for(int i=0;i<ans.size();i++)
- printf( "%d ",ans[i] );
- printf("\n");
- }
- else printf("No Answer!\n"); // ans 中的长度与n不相等,就说明无拓扑序列
来至于:https://blog.csdn.net/qq_41713256/article/details/80805338
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
上面部分是理论部分,下面看一下具体实践
很多题目其实可以转化为判断一个有向图是否有环的问题,比如leetcode上面的207. Course Schedule
根据上面的理论,解决该问题就轻而易举啦,代码如下(python)
- class Solution(object):
- def canFinish(self, numCourses, prerequisites):
- """
- :type numCourses: int
- :type prerequisites: List[List[int]]
- :rtype: bool
- """
- queue = []
- ins = [0 for i in range(numCourses)]
- for prerequisite in prerequisites:
- ins[prerequisite[0]]+=1
- for i in range(numCourses):
- if ins[i]==0:
- queue.append(i)
- count = len(queue)
- while queue:
- n = queue.pop(0)
- for prerequisite in prerequisites:
- if n== prerequisite[1]:
- ins[prerequisite[0]]-=1
- if ins[prerequisite[0]]==0:
- queue.append(prerequisite[0])
- count+=1
- return numCourses==count
同理下面这道题也很简单:210. Course Schedule II
它要求在判断有无环的同时,同时要求如果是无环,要返回我们上面说的拓扑序列,否者返回空
这里其实只需要再增加一个列表比如result,用其记录每次出队列的元素,如果最后判断是无环的话,就返回该列表,否者返回空列表即可
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。