使用鄰接表來表示有向圖,其中v->w表示爲頂點v對應的鄰接鏈表中包含一個w頂點。java
public class Digraph { private int V;//頂點數 private int E;//邊數 private Bag<Integer>[] adj;//鄰接表 public Digraph(int V) { this.V = V; adj = (Bag<Integer>[]) new Bag[V]; for(int i=0;i<adj.length;i++) adj[i] = new Bag<Integer>(); } public int V() {return V;} public int E() {return E;} public void addEdge(int v,int w) { adj[v].add(w); E++;} //頂點v所關聯的全部頂點 public Iterable<Integer> adj(int v){return adj[v];} //有向圖的反轉 public Digraph reverse() { Digraph R = new Digraph(V); for(int v=0; v<V;v++) for(int w : adj(v)) R.addEdge(w, v); return R; } }
有向圖的深度優先遍歷和廣度優先遍歷和無向圖如出一轍,能夠經過以前的無向圖遍歷問題來學習。https://my.oschina.net/HuoQibin/blog/1592318算法
拓撲排序:給定一幅有向圖,將全部頂點排序,使得全部的有向邊均從排在前面的元素指向排在後面的元素(或者說明沒法作到這一點)。數據結構
優先級限制下不該該存在有向環,一個優先級限制的問題若是存在有向環,那麼這個問題 確定是無解的。因此先來解決有向環檢測問題。post
採用深度優先遍從來解決這個問題:用一個棧表示「當前」正在遍歷的有向路徑上的頂點。一旦找到一條有向邊v->w,而且w已經存在於棧中,那麼就找到了一個環;若是沒有找到這條邊,那麼就是無環圖。學習
public class DirectedCycle { private boolean[] marked; private int [] edgeTo; private Stack<Integer> cycle;//有向環中全部頂點 private boolean[] onStack;//遞歸調用棧中全部頂點 public DirectedCycle(Digraph G) { //略 } //修改過的深度優先遍歷 public void dfs(Digraph G,int v) { onStack[v] = true; marked[v] = true; for(int w: G.adj(v)){ if(this.hasCycle()) return; //檢測出有環,算法中止 else if(!marked[v]) { edgeTo[w] = v; dfs(G,w); } else if(onStack[w]) { //若是爲真,則說明造成了環,把環路經保存下來 cycle = new Stack<Integer>(); for(int x = v;x!= w;x=edgeTo[x]) cycle.push(x); cycle.push(w); cycle.push(v); } } } public boolean hasCycle() { return cycle != null; } public Iterable<Integer> cycle(){ return cycle; } }
其實,將有向無環圖的深度優先遍歷的路徑記錄下來就是一種拓撲排序。遍歷的順序取決於數據結構的性質以及是在遞歸調用以前仍是以後保存。通常有三種排列排序:this
public class DepthFirstOrder{ private boolean[] marked; private Queue<Integer> pre;//全部頂點的前序排列 private Queue<Integer> post;//全部頂點的後序排列 private Stack<Integer> reversePost;//全部頂點的逆後序排列 //構造器 public DepthFirstOrder(Digraph G){ pre = new Queue<Integer>(); post = new Queue<Integer>(); revercePost = new Stack<Integer>(); marked = new boolean[G.V()]; for(int v = 0; v < G.V(); v++) if(!marked[v]) dfs(G,v); } //深度優先遍歷 private void dfs(Digraph G,int v) { pre.enqueue(v); marked[v] = true; for(int w:G.adj(v)) if(!marked[w]) dfs(G,w); post.enqueue(v); reversePost.push(v); } //返回遍歷結果 public Iterable<Integer> pre(){ return pre; } public Iterable<Integer> post(){ return post; } public Iterable<Integer> reversePost(){ return reversePost; } }
public class Tolpological { private Iterable<Integer> order; public Tolpological(Digraph G) { DirectedCycle cyclefinder = new DirectedCycle(G); //若是無有向環,則調用深度優先遍歷,最後輸出逆後序排列 if(!cyclefinder.hasCycle()) { DepthFirstOrder dfs = new DepthFirstOrder(G); order = dfs.reversePost(); } } public Iterable<Integer> order(){return order;} }