拓撲排序 - Topological Sort

殷老實發表於2016-10-02

有向圖的拓撲排序是它的頂點的線性排序,比如任意 邊 uv 對於 它的頂點 u 和 頂點 v, u v按照 u 在前 v在後的順序排序。 注意:只有 有向無環圖 (DAG) 才有拓撲排序, 如果一個圖 是有環 的圖那麼它不存在拓撲排序。


在現實生活中, 拓撲排序能被用於以下場景。 比如任意頂點都是要完成的任務,並且每個邊 都是 完成一個任務才能繼續完成下一個任務的 一種限制。 所以在這種情況下, 對這個圖的拓撲排序就是完成所有任務的序列佇列。


Directed acyclic graph 2.svg
The graph shown to the left has many valid topological sorts, including:
  • 5, 7, 3, 11, 8, 2, 9, 10 (visual left-to-right, top-to-bottom)
  • 3, 5, 7, 8, 11, 2, 9, 10 (smallest-numbered available vertex first)
  • 5, 7, 3, 8, 11, 10, 9, 2 (fewest edges first)
  • 7, 5, 11, 3, 10, 8, 9, 2 (largest-numbered available vertex first)
  • 5, 7, 11, 2, 3, 8, 9, 10 (attempting top-to-bottom, left-to-right)
  • 3, 7, 8, 5, 11, 10, 2, 9 (arbitrary)
以上就是一個例子。


Kahn's 演算法。演算法複雜度為 線性時間(邊的數 + 頂點的數)


L - 一個空的list,作為最後返回值
S - 一個Set包含所有的點,這些點沒有incoming 的邊。

while S is non-empty do:
	remove a node n from S
	add n to the list of L
	
	for each node m with an edge e from n to m do:
		remove edge e from graph
		
		if m has no other incoming edges then:
			insert m into S

if graph has edges then:
	return error ( 有環圖,不能進行拓撲排序 )
else 
	return L ( 解 )

Java 版本實現

package TopologicalSorting;

import java.util.*;

//this indicate an edge 
//from node_2 point to node_1
class Pair {
	String node_1;
	String node_2;
	Pair(String node_1, String node_2) {
		this.node_1 = node_1;
		this.node_2 = node_2;
	}
}

public class KahnAlgorithm {
	
	public static ArrayList<String> Kahn (ArrayList<Pair> graph) {
		ArrayList<String> L = new ArrayList<String> ();	//to store the result
		ArrayList<String> S = new ArrayList<String> ();	//set of nodes with no incoming edges
		
		S = findNoIncomingEdges (graph);
		
		while (!S.isEmpty()) {
			String n = S.remove(0);	//remove node n from S
			L.add(n);				//add node n to L
			
			//find all of the nodes that have an edge from n to m
			for (String m : findNodes (n,graph)) {
				//remove edge e from graph
				graph = removeEdge (m, n, graph);
				
				//if m does not has incoming edge
				if (!hasIncomingEdge(m, graph)) {
					//insert m into S
					S.add(m);
				}
				
			}
		}
		
		//still has edge left
		if (!graph.isEmpty()) {
			return null;
		}
		
		return L;
	}
	
	//find the nodes with no incoming edge
	public static ArrayList<String> findNoIncomingEdges (ArrayList<Pair> graph) {
		ArrayList<String> result = new ArrayList<String> ();
		Set<String> grandSet = new HashSet<String> ();	//all node in graph

		//construct grand set
		for (Pair temp : graph) {
			grandSet.add(temp.node_1);
			grandSet.add(temp.node_2);
		}
		
		result.addAll(grandSet);
		
		for (Pair temp : graph) {
			if (grandSet.contains(temp.node_1)) {
				result.remove(temp.node_1);
			}
		}
		
		return result;
	}

	//find the nodes  and remove all of the edges with n and m
	public static ArrayList<String> findNodes (String n, ArrayList<Pair> graph) {
		ArrayList<String> result = new ArrayList<String> ();
		
		//find the nodes that point from n to m
		for (Pair p : graph) {
			if (p.node_2.compareTo(n) == 0) {
				result.add(p.node_1);
			}
		}
		
		return result;
	}

	//remove edge from graph
	public static ArrayList<Pair>  removeEdge (String m, String n, ArrayList<Pair> graph) {
		ArrayList<Pair> result = new ArrayList<Pair> ();
		result = (ArrayList<Pair>) graph.clone();
		for (Pair temp : graph) {
			if (temp.node_1.compareTo(m) == 0 
					&& temp.node_2.compareTo(n) == 0) {
				result.remove(temp);
			}
		}
		return result;
	}
	
	//check whether it has incoming edge
	public static boolean hasIncomingEdge (String m, ArrayList<Pair> graph) {
		for (Pair temp : graph) {
			if (temp.node_1.compareTo(m) == 0) {
				return true;
			}
		}
		
		return false;
	}
}

有問題歡迎指出。




 

相關文章