拓扑排序介绍
拓扑排序是一个比较常用的图论算法,经常用于完成有依赖关系的任务的排序。比如如下图例题:
在图中,v2要想执行就必须先执行v3和v1,v0事件就可以直接执行,不需要依赖其他事件是否执行完成。依赖关系就好比打游戏,你要先选择英雄,才能进入游戏,v0就是选择英雄,v4就是进入游戏这个样子。拓扑排序就是来解决这样的问题,在不影响依赖关系的基础上,将图遍历。
代码实例
我们就以上面的例题为例子,将此图拓扑排序,我们需要用一种数据结构来表示图,在这里我们使用的是邻接表,如下图:
首先第一竖排是邻接顶点,也就是图中所有的点,由入度(in),数据域(data),第一个子节点(firstnode)组成,除了第一竖排之外的都是边表顶点由下标(adjvex),next组成。v0连接到v11,v5,v4,如图表示为v0的第一个子节点为v11,与v11相邻的是v5,与v5相邻的是v4。
算法思想
1.创建一个数组adjList来存放所有的邻接顶点,将图表示出来,如方法createGraph。
2.遍历数组,找到所有入度(in)为0的顶点,放入栈中。
3.在栈不为空的前提下:弹出栈顶元素,将弹出的邻接顶点后跟的所有边表顶点权值减去1,并且看是否有边表顶点的权值变为0,若有就将其放入栈中。如此不断执行知道栈为空。
算法思想还是比较简单的。
代码如下
import java.util.Stack;
/**
* 拓扑排序
* @author 65481
*
*/
public class DnGraphTopolgic {
//顶点个数
private int numvertexes;
//存放临界点的一维数组,接下来我们要对这个数组进行拓扑排序操作
private VertexNode[] adjList;
public DnGraphTopolgic(int numvertexes ) {
this.numvertexes = numvertexes;
}
//构建图
public void createGraph() {
VertexNode node0 = new VertexNode(0, "v0");
VertexNode node1 = new VertexNode(0, "v1");
VertexNode node2 = new VertexNode(2, "v2");
VertexNode node3 = new VertexNode(0, "v3");
VertexNode node4 = new VertexNode(2, "v4");
VertexNode node5 = new VertexNode(3, "v5");
VertexNode node6 = new VertexNode(1, "v6");
VertexNode node7 = new VertexNode(2, "v7");
VertexNode node8 = new VertexNode(2, "v8");
VertexNode node9 = new VertexNode(1, "v9");
VertexNode node10 = new VertexNode(1, "v10");
VertexNode node11 = new VertexNode(2, "v11");
VertexNode node12 = new VertexNode(1, "v12");
VertexNode node13 = new VertexNode(2, "v13");
adjList = new VertexNode[numvertexes];
//临接顶点构建
adjList[0] = node0;
adjList[1] = node1;
adjList[2] = node2;
adjList[3] = node3;
adjList[4] = node4;
adjList[5] = node5;
adjList[6] = node6;
adjList[7] = node7;
adjList[8] = node8;
adjList[9] = node9;
adjList[10] = node10;
adjList[11] = node11;
adjList[12] = node12;
adjList[13] = node13;
//边表顶点构建
node0.firstNode = new EdgeNode(11);
node0.firstNode.next = new EdgeNode(5);
node0.firstNode.next.next = new EdgeNode(4);
node1.firstNode = new EdgeNode(8);
node1.firstNode.next = new EdgeNode(4);
node1.firstNode.next.next = new EdgeNode(2);
node2.firstNode = new EdgeNode(9);
node2.firstNode.next = new EdgeNode(6);
node2.firstNode.next.next = new EdgeNode(5);
node3.firstNode = new EdgeNode(13);
node3.firstNode.next = new EdgeNode(2);
node4.firstNode = new EdgeNode(7);
node5.firstNode = new EdgeNode(12);
node5.firstNode.next = new EdgeNode(8);
node6.firstNode = new EdgeNode(5);
node8.firstNode = new EdgeNode(7);
node9.firstNode = new EdgeNode(11);
node9.firstNode.next = new EdgeNode(10);
node10.firstNode = new EdgeNode(13);
node12.firstNode = new EdgeNode(9);
}
/**
* 拓扑排序
*/
public void topologicalSort() {
//需要使用栈的数据结构,至于为什么用integer,因为我们这个栈中放的既要有边表顶点,又要有临接顶点,为了一视同仁
Stack<Integer> stack = new Stack<>();
//操作数 如果操作数小于点的个数,那么就是拓扑排序失败 也就是图不满足拓扑排序的前提
int count = 0;
//记录下标
int k ;
//遍历,将入度为0的点放入栈中
for (int i = 0;i < numvertexes;i ++) {
if(adjList[i].in == 0) {
stack.push(i);
}
}
while(!stack.isEmpty()) {
//弹出栈中第一个点
int pop = stack.pop();
System.out.println("顶点"+adjList[pop].data);
count ++;
//将这个点后面的边表顶点的入度减去1
for(EdgeNode node = adjList[pop].firstNode;node != null;node = node.next) {
k = node.adjVert;
if (--adjList[k].in == 0) {
stack.push(k);
}
}
}
if (count < numvertexes) {
System.out.println("图不符合拓扑排序条件");
}
}
public static void main(String[] args) {
DnGraphTopolgic dnGraphTopolgic = new DnGraphTopolgic(14);
dnGraphTopolgic.createGraph();
dnGraphTopolgic.topologicalSort();
}
//边表顶点
class EdgeNode{
//顶点下标
private int adjVert;
//边表顶点之后的边表顶点
private EdgeNode next;
//权重
private int weight;
public EdgeNode(int adjVert) {
this.adjVert = adjVert;
}
public int getAdjVert() {
return adjVert;
}
public void setAdjVert(int adjVert) {
this.adjVert = adjVert;
}
public EdgeNode getNext() {
return next;
}
public void setNext(EdgeNode next) {
this.next = next;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
//临接顶点
class VertexNode{
//入度
private int in;
//顶点的数据
private String data;
//临接顶点的第一个边表顶点
private EdgeNode firstNode;
public VertexNode(int in,String data) {
this.in = in;
this.data = data;
}
public int getIn() {
return in;
}
public void setIn(int in) {
this.in = in;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public EdgeNode getFirstNode() {
return firstNode;
}
public void setFirstNode(EdgeNode firstNode) {
this.firstNode = firstNode;
}
}
}
运算结果
顶点v3
顶点v1
顶点v2
顶点v6
顶点v9
顶点v10
顶点v13
顶点v0
顶点v4
顶点v5
顶点v8
顶点v7
顶点v12
顶点v11