任務管理系統算法-Kahn’s algorithm for Topological Sorting(一)

上一遍分析瞭如何設計任務管理系統的算法,拓撲排序之任務管理系統思路設計

今天我們就利用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。

這個任務管理系統還有尋找任務等級,找出任務依賴中存在的環等功能,下一篇再寫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章