拓扑排序
什么是拓扑排序
它是一个有向无环图的所有顶点的线性序列,也就是说,如果有向图中存在环,那么它就没有拓扑排序。除此之外,这个线性序列的特点有:
- 若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面
- 每个顶点只出现一次
DFS求解拓扑排序
下面是使用深度优先搜索(DFS)求解一个图的拓扑排序
以一个例题为例子:leetcode-207
这里我们将每一个课程看成是一个节点,每个节点有三种状态
- 已经搜索过的节点,设置为2
- 正在搜索的节点,设置为1,如果搜索过程中又碰到节点是这个状态,说明这个图中存在环,比如遍历顺序如下:1->2->3->1,因为1的状态之前已经是1了,下次再搜索到它就说明存在环了
- 还没有搜索的节点,设置为0
具体思路如下:
1.首先将有向图构造好,我们这里用的是一个数组(bian)来存放当前节点的相邻节点
2.遍历每一个节点,对每一个节点,判断它是否已经被搜索过,没有被搜索过则遍历该节点的相邻节点,对相邻节点的状态进行判断,具体如下:
- 如果相邻节点没有被搜索过,则将其状态设置为1,说明这个节点还在搜索当中,然后继续往下搜索,寻找该节点的相邻节点
- 如果相邻节点的状态为1,说明之前它已经被搜索过了,出现了环,则直接退出
- 相邻节点都遍历完后将当前节点的状态设置为2,说明已经搜索过了
代码如下:
class Solution {
List<List<Integer>> bian;
int vis[];
boolean res = true;
public boolean canFinish(int numCourses, int[][] prerequisites) {
//利用拓扑排序算法
//先构造一个有向图
bian = new ArrayList<List<Integer>>();
for(int i=0;i<numCourses;i++){
bian.add(new ArrayList<Integer>());
}
//加入边,即构造有向图
int n=prerequisites.length;
for(int i=0;i<n;i++){
int mid[]=prerequisites[i];
bian.get(mid[1]).add(mid[0]);//说明mid[1]课程完成后才能是mid[0]
}
vis = new int[numCourses];//初始化都是0,表明都没有搜索过
//变量每一个顶点,即课程
for(int i=0;i<numCourses;i++){
if(vis[i]==0){
dfs(i);//以该顶点为开始,进行深度优先遍历
}
}
return res;
}
public void dfs(int dot){
//遍历该顶点相邻的下一个点
List<Integer> mid=bian.get(dot);
int l=mid.size();
if(!res){//说明已经是有环了,退出循环
return;
}
for(int i=0;i<l;i++){
//判断是否遍历过
int tmp=mid.get(i);
if(vis[tmp]==0){
vis[tmp]=1;//说明这个点正在搜索中
dfs(tmp);//继续搜索
if(!res){//说明已经是有环了,退出循环
return;
}
}
else if(vis[tmp]==1){
res=false;//说明有环存在了
return;
}
}
vis[dot]=2;//表示这个已经搜索完了
}
}