拓扑排序的介绍:
- 拓扑排序:将有向图中的顶点以线性方式进行排序。即对于任何自顶点u到顶点v的有向边u->v ,在最后的排序结果中,顶点u总是在顶点v的前面。这样的排序结果,称为拓扑序。
- 有环图,不存在拓扑排序。
- 对于一个有向图,可能有多个拓扑排序的结果。
- 如下图的拓扑排序有两种情况:
1->2->3->4->5
1->2->3->5->4
拓扑排序的代码实现:
要实现拓扑排序的有向图:
实现思路:
- 将有向图转化为邻接矩阵,方便查找两个点之间是否有箭头。
- 首先遍历每一个顶点,尝试以每一个顶点为起点开始进行深搜,到深搜到最底层,也就是当碰到一个没有出度的顶点时,则这个顶点肯定是可以放在最后的,这时我们将这个顶点放到数组末尾(压栈)。
- 在深搜的过程中要标记,标记有三种:0表示还没有深搜过的顶点,1表示已经进行过深搜并且已经放入数组中(压栈),-1表示当前深搜正在遍历的顶点。之所以添加第三种标记-1是为了在深搜的过程中判断是否出现有环图。
Java代码:
public class 拓扑排序 {
//用临街矩阵表示有向图
private static int[][] grash = {
{0,1,0,0},
{0,0,0,0},
{0,1,0,0},
{0,0,1,0}
};
//标记访问状态,1:已经访问并返回,0:从未被访问, -1:正在访问
private static int[] visit = new int[4];
private static char[] v = {'a','b','c','d'}; //顶点内容
private static int n = 4; //顶点的个数
private static int[] topo = new int[n]; //拓扑排序的结果
private static int t = n; //标记topo数组到了哪一位
public static void main(String[] args) {
for(int i=0; i<n; i++){
if(visit[i]==1){// 以访问过则跳过
continue;
}
if(!dfs(i)){
System.out.println("有向图存在环,不存在拓扑排序");
}
}
//打印排序后的结果
for(int i=0; i<n; i++){
System.out.print(v[topo[i]] + " ");
}
}
//深搜遍历,返回false表示存在环,不存在拓扑排序
private static boolean dfs(int i) {
visit[i] = -1; //标记此次深搜访问过的顶点
//尝试寻找与其他顶点的有向边(是否有出度)
for(int j=0; j<n; j++){
if(grash[i][j]>0){ //顶点i到j右箭头
if(visit[j]==-1){
//此处,关于顶点j的递归还没有退出,前驱的状态是-1,后继的状态也是-1,
//说明在此次递归中早就路过了j,现在是第二次路过j
//一次递归路过两次同一顶点,只有一种情况,形成了环,所以返回false
return false;
}
/* if(visit[j]==0){//这样写不行,因为不能在没有进行标记的情况下return true
return dfs(j);
} */
if(visit[j]==0 && dfs(j)==false)
return false;
}
}
//如果没有出度则将顶点放入数组
topo[--t] = i;
visit[i] = 1;
return true;
}
}