您的位置:首页 > 编程语言 > Java开发

拓扑排序及其Java实现

2016-07-27 16:14 399 查看
拓扑排序是针对有向无圈图的顶点的一种排序,使得如果存在一条从A到B的路径,那么在排序中A必定在B的前面。

拓扑排序的应用场景很好理解,比如在内存中运行着很多任务,某个任务A的执行依赖于另外一个任务B,那么在A执行完之前,B一定不能被清理。而另外一些任务是没有关联的,如何来安排这些任务被清理的顺序就需要依赖拓扑排序。

一个简单的拓扑排序的方案(Kahn算法)是:先找出任意一个没有入边的顶点,然后打印该顶点,并将它及其边一起从图中剔除,然后对其余的部分继续这个操作

这个思路很简单,下面来做具体的实现。针对一个图,如下,来做拓扑排序,使用Java来实现。



需要新建三个类:节点、图、Kahn算法

具体的代码和注释如下:

package com.algorithm;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

/**
* 拓扑排序,当前方案并没有在节点类中加入过多的内容
* 但是在图类中加入了边的集合adjaNode
*/
public class TopoSortB {

/**
* 拓扑排序节点类
*/
private static class Node {
public Object val;
public int pathIn = 0; // 入链路数量
public Node(Object val) {
this.val = val;
}
}

/**
* 拓扑图类
*/
private static class Graph {
// 图中节点的集合
public Set<Node> vertexSet = new HashSet<Node>();
// 相邻的节点,纪录边
public Map<Node, Set<Node>> adjaNode = new HashMap<Node, Set<Node>>();

// 将节点加入图中
public boolean addNode(Node start, Node end) {
if (!vertexSet.contains(start)) {
vertexSet.add(start);
}
if (!vertexSet.contains(end)) {
vertexSet.add(end);
}
if (adjaNode.containsKey(start)
&& adjaNode.get(start).contains(end)) {
return false;
}
if (adjaNode.containsKey(start)) {
adjaNode.get(start).add(end);
} else {
Set<Node> temp = new HashSet<Node>();
temp.add(end);
adjaNode.put(start, temp);
}
end.pathIn++;
return true;
}
}

//Kahn算法
private static class KahnTopo {
private List<Node> result; // 用来存储结果集
private Queue<Node> setOfZeroIndegree; // 用来存储入度为0的顶点
private Graph graph;

//构造函数,初始化
public KahnTopo(Graph di) {
this.graph = di;
this.result = new ArrayList<Node>();
this.setOfZeroIndegree = new LinkedList<Node>();
// 对入度为0的集合进行初始化
for(Node iterator : this.graph.vertexSet){
if(iterator.pathIn == 0){
this.setOfZeroIndegree.add(iterator);
}
}
}

//拓扑排序处理过程
private void process() {
while (!setOfZeroIndegree.isEmpty()) {
Node v = setOfZeroIndegree.poll();

// 将当前顶点添加到结果集中
result.add(v);

if(this.graph.adjaNode.keySet().isEmpty()){
return;
}

// 遍历由v引出的所有边
for (Node w : this.graph.adjaNode.get(v) ) {
// 将该边从图中移除,通过减少边的数量来表示
w.pathIn--;
if (0 == w.pathIn) // 如果入度为0,那么加入入度为0的集合
{
setOfZeroIndegree.add(w);
}
}
this.graph.vertexSet.remove(v);
this.graph.adjaNode.remove(v);
}

// 如果此时图中还存在边,那么说明图中含有环路
if (!this.graph.vertexSet.isEmpty()) {
throw new IllegalArgumentException("Has Cycle !");
}
}

//结果集
public Iterable<Node> getResult() {
return result;
}
}

//测试
public static void main(String[] args) {
Node A = new Node("A");
Node B = new Node("B");
Node C = new Node("C");
Node D = new Node("D");
Node E = new Node("E");
Node F = new Node("F");

Graph graph = new Graph();
graph.addNode(A, B);
graph.addNode(B, C);
graph.addNode(B, D);
graph.addNode(D, C);
graph.addNode(E, C);
graph.addNode(C, F);

KahnTopo topo = new KahnTopo(graph);
topo.process();
for(Node temp : topo.getResult()){
System.out.print(temp.val.toString() + "-->");
}
}

}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: