拓扑排序:
概述:
一个概念的诞生离不开实际问题的提出。大学有很多门课,其中一些课刚上大学就可以学,有一些需要一些先修课,我们要怎么安排上课的顺序来保证上课的效率。如何解决这个实际问题,就引出了拓扑排序。
拓扑排序是对有向无环图进行排序的一种算法。有向:显而易见,我们的先修关系是需要有方向的,所以图一定要是有向的。无环:如果两门课互为先修课,那么该问题就无解。所以要保证这个图的无环的。
具体如何对图进行排序,我们这里介绍两种方案:
-
基于深度优先查找的一个简单应用:执行一次DFS遍历,并记住顶点变成死端(既退出遍历栈)的顺序,将该次序反过来就得到拓扑排序的一个解,当然,在遍历的时候不能遇到回边。如果遇到一条回边,该图就不是有向无环图,并且对它顶点的拓扑排序是不可能的。
-
第二种方案是基于减治技术的一个直接实现,不断地做一件事,在余下的所有图中找到源头,它是一个没有输入的节点,然后把它和所有从它出发的边都删除,顶点被删除的次序就是拓扑排序问题的一个解。
-
这里我们仅仅使用第二种方案进行实现:
Java代码:
package com.result.chart;
import java.util.Stack;
/**
* @program:算法库
* @description:拓扑排序实现类
*/
public class TopologicalSorting {
//各个节点的前驱节点的个数
int[] NumberOfPrecursorNodes;
public void result(DenseGraph denseGraph){
//用于存储节点信息的数组
boolean[][] g = denseGraph.getG();
//图中总的节点数
int node = denseGraph.findNode();
//实例化
NumberOfPrecursorNodes = new int[node];
//初始化我们存储前驱节点个数的数组
for (int i = 0; i < node; i++) {
for (int j = 0; j < node; j++) {
if(g[i][j]){
NumberOfPrecursorNodes[j]++;
}
}
}
//开辟栈空间
Stack<Integer> stack = new Stack<>();
//将所有前驱节点个数为0的节点入栈
for (int i = 1; i < node; i++) {
if(NumberOfPrecursorNodes[i]==0){
stack.push(i);
}
}
//开始遍历
while(!stack.empty()){
//弹出一个元素
int temp = stack.pop();
//输出
System.out.print(temp+" ");
//将以该元素为前驱的节点的前驱节点数减一
for (int i = 0; i < node; i++) {
if(g[temp][i]){
NumberOfPrecursorNodes[i]--;
//维护数量数组,一旦等于0了之后立刻入栈
if(NumberOfPrecursorNodes[i]==0){
stack.push(i);
}
}
}
}
}
}
关于图的DenseGraph 类代码:
/**
* @program:算法库
* @description:稠密图的创建,使用邻接矩阵
*/
public class DenseGraph {
private int edge; //边数
private int node; //节点数
private boolean direction; //是否为有向图
private boolean[][] g; //图的具体数据
public boolean[][] getG() {
return g;
}
public DenseGraph(int node, boolean direction){
assert node>=0; //一个断言
this.node = node;
this.direction = direction;
this.g = new boolean[node][node];
}
//返回边数
public int findEdge(){
return edge;
}
//返回节点数
public int findNode(){
return node;
}
//为图添加一条边
public void addEdge(int p,int q){
assert p>=0&&p<node;
assert q>=0&&q<node;
if(hasEdge(p,q)){
return;
}
g[p][q]=true;
if(!direction){
g[q][p]=true;
}
edge++;
}
//检验p到q是否有一条边
public boolean hasEdge(int p,int q){
assert p>=0&&p<node;
assert q>=0&&q<node;
return g[p][q];
}
}