https://blog.csdn.net/weixin_42001089/article/details/84327306
定义:
直接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),两者不相同证明有环,同时注意有环的时候是没有拓扑排序的即没有拓扑排序的
--------------------------------------------------------------------------------------------------------------------------------------------------------------
public boolean canFinish(int numCourses, int[][] prerequisites) {
int ret = 0;
Queue<Integer> q = new LinkedList<>();
if(numCourses <= 1)
return true;
int row = prerequisites.length;
int[] map = new int[numCourses];
for(int i=0; i<row; i++) {
map[prerequisites[i][1]]++;
}
for(int i=0; i<numCourses; i++) {
if(map[i] == 0) {
q.offer(i);
}
}
while(!q.isEmpty()) {
int target = q.poll();
ret++;
for(int i=0; i<row; i++) {
if(prerequisites[i][0] == target) {
map[prerequisites[i][1]] --;
if(map[prerequisites[i][1]] == 0) {
q.offer(prerequisites[i][1]);
}
}
}
}
if(ret == numCourses)
return true;
return false;
}
法2:DFS
https://www.cnblogs.com/MrSaver/p/9996941.html
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
for(int i=0;i<numCourses;i++)
graph.add(new ArrayList<>());
for(int i=0;i<prerequisites.length;i++)
{
int course = prerequisites[i][0];
int pcourse = prerequisites[i][1];
graph.get(course).add(pcourse);
}
int[] visited = new int[numCourses];
for(int i=0;i<numCourses;i++)
if(DFS(i,graph,visited))
return false;
return true;
}
public boolean DFS(int curr,ArrayList<ArrayList<Integer>> graph,int[] visited)
{
//递归结束条件
if(visited[curr]==1)//这个节点已经被访问
return true;
if(visited[curr]==2)//这个节点没有被访问
return false;
//业务逻辑处理
visited[curr]=1;//表示正在访问
for(int id:graph.get(curr))
if(DFS(id,graph,visited))
return true;
visited[curr]=2;//表示已经访问
return false;
}
}