一、 有向圖概述
和無向圖不同,有向圖是具有指向性的圖,是由一組頂點和若干有方向的邊組成,每個有方向的邊都連着兩個有序的頂點。向有向圖添加一條邊時,只會根據指向有頂點v新增一條指向w的邊
不需要w->v再添加一條。
二、 有向圖定義與實現
出度:某個頂點指出的邊的條數為該頂點的出度
入度:指向某個頂點的邊的條數為該頂點的入度
有向路徑:由一組頂點和若干有方向的邊組成,每個有方向的邊都連着兩個有序的頂點,稱為有向路徑。
有向環:至少含有一條邊,且起點和終點相同的有向圖
import com.data.struct.common.list.queue.Queue;
/** * 有向圖實現 * @author jiyukai */ public class Digraph {
//定點個數 public int V;
//邊的數量 public int E;
//圖的鄰接表 public Queue<Integer>[] qTable;
public Digraph(int v) { this.V = v;
this.E = 0;
//初始化鄰接表,數組中的索引為頂點,值為已隊列,存放相鄰的頂點 qTable = new Queue[v]; for(int i=0;i<v;i++) { qTable[i] = new Queue<Integer>(); } }
/** * 向圖中添加一條有向邊 * @param v * @param w */ public void addEdge(int v,int w) { //頂點v添加w的指向 qTable[v].enqueue(w);
//邊加1 E++; }
/** * 返回當前頂點的數量 * @return */ public int V() { return V; }
/** * 返回當前邊的數量 * @return */ public int E() { return E; }
/** * 獲取與頂點V相鄰的頂點 * @param V * @return */ public Queue adjoin(int V) { return qTable[V]; }
/** * 獲取該有向圖的反向圖 * @return */ public Digraph reverse() { Digraph digraph = new Digraph(V); for(int v=0;v<V;v++) { for(int w : digraph.qTable[v]) { //上述有向圖實現為添加v-w的有向邊,現在反着添加w-v的有向邊即可 digraph.addEdge(w, v); } } return digraph; } } |
三、 拓撲圖概述
每天上下班的地鐵可能涉及轉車,把每個站看成一個頂點,則頂點的到達是有序,如下圖,要去地鐵站5,則需有序的從1轉到2轉到3轉到4,最后再轉站到5,這樣也形成了一副有向圖
對於一副有向圖,若存在若干有序的頂點,且每條有向邊都從排序靠前的頂點指向排序靠后的頂點,這樣的圖為經過拓撲排序后的有向圖。
四、 有向環檢測
如下圖,存在2-3-4-2的有向環,所以在解決拓撲圖這種帶有頂點優先級的問題時,需要檢測圖中是否存在有向環,避免陷入死循環。
在有向環的檢測中,我們可以開辟一個數組,索引為頂點,值為布爾值,對於搜索過的頂點我們標記為true,未搜索過的初始化為false,當搜索到某一個頂點時,若發現它的值為true
則證明在之前已被搜索標記過,此時可以判斷存在有向環
import com.data.struct.common.graph.Graph;
/** * 檢測有向圖中是否有環 * @author jiyukai */ public class DirectedCycle {
//標記頂點x是否有被搜索過 public boolean[] flags;
public int count;
//記錄圖中是否有環 private boolean hasCycle;
/** * 深度優先搜索,找出與頂點V想通的所有頂點 * @param G * @param V */ public DirectedCycle(Graph G,int V) { //長度置0 this.count = 0;
//創建一個與頂點數量相同的標記數組,標記每個頂點是否被搜索過 flags = new boolean[G.V()];
//初始化默認無環 hasCycle = false;
//深度優先搜索,檢測有環需遍歷圖中每一個點 for(int v=0;v<V;v++) { if(!flags[v]) { dfs(G, v); } } }
/** * 深度優先搜索實現 * @param G * @param V */ public void dfs(Graph G,int V) { //被搜的頂點V標記為搜索過 flags[V] = true;
for(int w : G.qTable[V]) { if(!flags[w]) { dfs(G, w); }
if(flags[w]) { hasCycle = true; return; } }
//相通的點+1 count++; }
/** * 返回與頂點V相通的所有頂點 * @return */ public int count() { return count; }
/** * 判斷頂點w是否與v相通 * @param w * @return */ public boolean isConnected(int w) { return flags[w]; }
} |
五、 基於深度優先的頂點搜索實現拓撲排序
我們可以基於圖的深度優先搜索來找出一副有向圖中按順序排列的頂點,開辟一個棧用於存放有序頂點的集合,從一個頂點開始遞歸深度搜索它的鄰接頂點,搜索到不再具備遞歸條件
即到達最后一個頂點時,將頂點入棧,作為優先級最靠后的頂點,同時將該頂點標記為已搜索;之后依次將搜索的頂點入棧,若搜索的頂點已被標記,則不重復入棧,最后將棧中的頂點依次取出
則可以找出一副有向圖中按順序排列的頂點。
import com.data.struct.common.graph.Graph; import com.data.struct.common.list.stack.Stack;
/** * 基於深度優先搜索的拓撲排序 * @author jiyukai */ public class DepthFirstOrder {
//標記頂點x是否有被搜索過 public boolean[] flags;
public int count;
//使用棧,存儲頂點序列 private Stack<Integer> stack;
/** * 深度優先搜索,找出與頂點V想通的所有頂點 * @param G * @param V */ public DepthFirstOrder(Graph G,int V) { //長度置0 this.count = 0;
//創建一個與頂點數量相同的標記數組,標記每個頂點是否被搜索過 flags = new boolean[G.V()];
//深度優先搜索,檢測有環需遍歷圖中每一個點 for(int v=0;v<V;v++) { if(!flags[v]) { dfs(G, v); } }
}
/** * 深度優先搜索實現 * @param G * @param V */ public void dfs(Graph G,int V) { //被搜的頂點V標記為搜索過 flags[V] = true;
for(int w : G.qTable[V]) { if(!flags[w]) { dfs(G, w); } }
//頂點進棧 stack.push(V);
//相通的點+1 count++; }
/** * 返回與頂點V相通的所有頂點 * @return */ public int count() { return count; }
/** * 判斷頂點w是否與v相通 * @param w * @return */ public boolean isConnected(int w) { return flags[w]; }
/** * 獲取頂點的順序棧 * @return */ public Stack<Integer> getOrderStack(){ return stack; }
} |