有向圖問題1--深度優先、廣度優先遍歷和拓撲排序

有向圖基礎

術語定義:

  • 一個頂點的出度爲由該頂點指出的邊的總數
  • 一個頂點的入度爲指向該頂點的邊的總數
  • 一條有向邊的第一個頂點稱爲它的頭,第二個頂點稱爲它的尾

數據結構:

使用鄰接表來表示有向圖,其中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;}
}
  • 一幅有向無環圖的拓撲排序即爲全部頂點的逆後序排序。
  • 使用深度優先搜索對有向無環圖進行拓撲排序須要的時間和V+E成正比。
相關文章
相關標籤/搜索