上一遍分析了如何设计任务管理系统的算法,拓扑排序之任务管理系统思路设计。
今天我们就利用Kahn’s algorithm for Topological Sorting来实现任务管理系统算法。
设计一个数据结构Graph类来储存各个task之间的依赖关系,并根据每个task相互的依赖关系找出任务排列顺序。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class Graph {
private int V; // No. of vertices
private int minimumLevel;
private int tasksInLevel; // store the number of tasks in the same level
private List<ArrayList<Integer>> adj;
private List<String> tasks;
private boolean isUnique = true;
private boolean isCircle = false;
private Queue<Integer> mCircleQueue = new LinkedList<Integer>(); // store the circle tasks in order
private boolean isFound = false; // If we find circle, then isFound is true
public Graph() {
this.V = 0;
adj = new ArrayList<ArrayList<Integer>>();
tasks = new ArrayList<String>();
}
public Graph(String loadTasks[]) {
this.V = loadTasks.length;
adj = new ArrayList<ArrayList<Integer>>();
for(int i = 0; i < V; i++) {
adj.add(new ArrayList<Integer>());
}
tasks = new ArrayList<String>();
for(String task : loadTasks) {
tasks.add(task);
}
}
public void addTask(String s) {
if(!tasks.contains(s)) {
adj.add(new ArrayList<Integer>());
this.V++;
tasks.add(s);
} else {
System.out.println("Task " + s + " is already in the system, please add another task or quit.");
}
}
// Add dependency for last added task
public void addDependency(String s) {
if(!tasks.contains(s)) {
System.out.println("Task " + s + " is not in the system, please try another task as dependency.");
} else {
int u = tasks.indexOf(s);
int v = tasks.size() - 1;
addEdge(u, v);
}
}
/**
* Add dependency for specific task
* @param dependency
* @param task
*/
public void addDependency(String dependency, String task) {
if(!tasks.contains(dependency)) {
System.out.println("Task " + dependency + " is not in the system, please try another task.");
} else if(!tasks.contains(task)) {
System.out.println("Task " + task + " is not in the system, please try another task.");
} else {
int u = tasks.indexOf(dependency);
int v = tasks.indexOf(task);
addEdge(u, v);
}
}
// public void makeTaskDependOnLast(String s) {
// if(!tasks.contains(s)) {
// System.out.println("Task " + s + " is not in the system, please try another task.");
// } else {
// int u = tasks.size() - 1;
// int v = tasks.indexOf(s);
// addEdge(u, v);
// }
// }
/**
* Add an edge to graph
* @param u is dependency
* @param v is depend on u
*/
public void addEdge(int u, int v) {
if(!adj.get(u).contains(v))
adj.get(u).add(v);
}
public void topologicalSort() {
isUnique = true;
isCircle = false;
minimumLevel = 0;
tasksInLevel = 0;
// Create an array to store all vertices' indegrees.
int indegree[] = new int[V];
// Find all indegree for all vertices
for(int i = 0; i < V; i++) {
ArrayList<Integer> temp = adj.get(i);
for(int node: temp) {
indegree[node]++;
}
}
// A queue store all vertices which indegree are 0
Queue<Integer> queue = new LinkedList<Integer>();
for(int i = 0; i < indegree.length; i++) {
if(indegree[i] == 0) {
queue.add(i);
}
}
tasksInLevel += queue.size();
if(tasksInLevel > 0)
minimumLevel++;
if(queue.size() > 1) {
isUnique = false;
}
int count = 0;
// ArrayList to store topological sort result
ArrayList<Integer> topOrder = new ArrayList<Integer>();
while(!queue.isEmpty()) {
int u = queue.poll();
topOrder.add(u);
count++;
for(int node : adj.get(u)) {
indegree[node]--;
if(indegree[node] == 0) {
queue.add(node);
}
}
tasksInLevel--;
if((tasksInLevel) == 0) {
tasksInLevel += queue.size();
if(tasksInLevel > 0)
minimumLevel++;
}
if(isUnique && queue.size() > 1) {
isUnique = false;
}
}
if(count != V) {
isCircle = true;
// An ArrayList store all tasks which are in circle
Queue<Integer> tasksRemain = new LinkedList<Integer>();
System.out.println("There is NO valid ordering of task.");
System.out.print("There is a cycle: ");
for(int i = 0; i < indegree.length; i++) {
if(indegree[i] > 0) {
tasksRemain.add(i);
}
}
while(!tasksRemain.isEmpty()) {
mCircleQueue.clear();
mCircleQueue.add(tasksRemain.poll());
searchCircle(mCircleQueue.peek(), indegree);
if(isFound) {
isFound = false;
break;
}
}
while(!mCircleQueue.isEmpty()) {
System.out.print(tasks.get(mCircleQueue.poll()) + " ");
}
System.out.println();
} else {
// Print topological order
System.out.println("A valid ordering of tasks is as follows:");
for(int i : topOrder) {
System.out.print(tasks.get(i)+" ");
}
System.out.println();
if(!isUnique()) {
System.out.println("This is NOT the only valid ordering of task.");
} else {
System.out.println("This is the ONLY valid ordering of task.");
}
System.out.println("The minimum number of levels is "+ minimumLevel + ".");
}
}
private void searchCircle(int u, int[] indegree) {
if(adj.get(u).size() > 0) {
for(int dependency : adj.get(u)) {
if(indegree[dependency] > 0 && !isFound) {
if(mCircleQueue.element().equals(dependency)) {
isFound = true;
mCircleQueue.add(dependency);
return;
} else {
mCircleQueue.add(dependency);
searchCircle(dependency, indegree);
if(!isFound)
((LinkedList<Integer>) mCircleQueue).removeLast();
}
}
}
}
}
public boolean isUnique() {
return isUnique;
}
public void setUnique(boolean isUnique) {
this.isUnique = isUnique;
}
public boolean isCircle() {
return isCircle;
}
public void setCircle(boolean isCircle) {
this.isCircle = isCircle;
}
}
adj是List<ArrayList<Integer>>类用于存储各个任务之间的依赖关系
/**
* Add an edge to graph
* @param u is dependency
* @param v is depend on u
*/
public void addEdge(int u, int v) {
if(!adj.get(u).contains(v))
adj.get(u).add(v);
}
在addEdge方法里体现了u和v之间的依赖关系,即u是v的前提,u必须要在v之前完成,在图里面可以表示为u->v
在topologicalSort()方法里,通过遍历adj数据结构找到各个task的入度indegree.
// Find all indegree for all vertices
for(int i = 0; i < V; i++) {
ArrayList<Integer> temp = adj.get(i);
for(int node: temp) {
indegree[node]++;
}
}
例如u->v是u和v之间的依赖关系,v依赖于u,那么在这里v的入度是1,u是0,v在这里有一个箭头指向自己,那么入度为1.
入度数目取决于当前task被多少箭头指向,也就是当前task依赖于多少个其他task。
// A queue store all vertices which indegree are 0
Queue<Integer> queue = new LinkedList<Integer>();
for(int i = 0; i < indegree.length; i++) {
if(indegree[i] == 0) {
queue.add(i);
}
}
tasksInLevel += queue.size();
if(tasksInLevel > 0)
minimumLevel++;
if(queue.size() > 1) {
isUnique = false;
}
int count = 0;
// ArrayList to store topological sort result
ArrayList<Integer> topOrder = new ArrayList<Integer>();
while(!queue.isEmpty()) {
int u = queue.poll();
topOrder.add(u);
count++;
for(int node : adj.get(u)) {
indegree[node]--;
if(indegree[node] == 0) {
queue.add(node);
}
}
tasksInLevel--;
if((tasksInLevel) == 0) {
tasksInLevel += queue.size();
if(tasksInLevel > 0)
minimumLevel++;
}
if(isUnique && queue.size() > 1) {
isUnique = false;
}
}
queue就是储存入度为0的tasks,topOrder储存的是最后topological sort的顺序也就是任务完成顺序。
在while循环里每次poll queue里面的一个task储存到topOrder中,当然这个task入度为0,并且将和这个task有依赖关系的其他task的入度减1,因为我们已经开始把这个task取出并加入到将要完成的任务序列中,相应的indegree表也要更新一下,每次取出的task都可能会产生新的indegree为0的tasks。
while循环就是重复以上步骤,每次从queue取出入度为0的task加入到topOrder中,然后产生的新的入度为0的tasks加入到queue中,在while循环中不断取出入度为0的task直到queue已经没有task了,说明根据依赖关系能完成的tasks全部取出了。
此时如果while循环结束还有task存在我们加入到task manager系统中,也就是代码中的count如果小于V(tasks总数目),说明这个任务依赖关系存在环,也就是有一部分tasks彼此依赖,我们不能找到剩下的任何task不依赖任务task,从而不能完成剩下的tasks。
这个任务管理系统还有寻找任务等级,找出任务依赖中存在的环等功能,下一篇再写。

本文介绍如何利用Kahn’s algorithm进行任务管理系统的拓扑排序。通过设计Graph类存储任务依赖关系,实现任务的入度计算,并使用队列进行任务排序。在循环中不断取出入度为0的任务,直到所有无环任务完成。若有未完成任务,则表示存在依赖环。
158

被折叠的 条评论
为什么被折叠?



