有向圖基本演算法 -- 遍曆演算法
1. 圖的表示
2. 有向圖的遍曆演算法:深度優先
3. 有向圖的遍曆演算法:廣度優先
4 代碼反思
5. 下載
1. 圖的表示
1.1 圖的定義
圖G定義為V和E的集合G={V, E},其中V表示圖中的所有的頂點集合,E表示的是G中的所有的邊的集合。圖按照E中的元素是否有方向,分為有向圖和無向圖。
1.2 圖的表示方法
上面給出的數學的定義,那麼在電腦中如何表示圖?通常意義上,有下面的兩種方法:鄰接表和鄰接矩陣標記法。
無向圖的鄰接表和鄰接矩陣表示如下所示:
有向圖的鄰接表和鄰接矩陣表示如下所示:
根據上面的表示方法,下面定義圖G的這種資料結構(鄰接表),首先定義圖的頂點GraphVertex:
// 頂點顯示的符號 public char Symbol { get; set; } // 頂點使用中色彩 public VertexColor Color { get; set; } // 頂點和開始節點之間的距離 public int Distance { get; set; } // 廣度遍曆父節點 public GraphVertex Parent { get; set; } // 深度優先搜尋中的開始時間 public int StartTime { get; set; } // 深度優先搜尋中的結束時間 public int FinishTime { get; set; } // 頂點對應的邊
public List<GraphEdge> FollowEdges { get; set; }
定義圖G的邊的資料結構:
// 邊開始頂點,在鄰接表的儲存中其實沒有必要儲存 public GraphVertex From { get; set; } // 結束頂點 public GraphVertex To { get; set; } // 邊權重
public int Weight { get; set; }
定義圖:
// 資料成員,這裡假設的是頂點的symbol是各個不相同的 private Hashtable graph = new Hashtable();
private int time = 0;
整體上的結構如下:
2. 有向圖的深度優先演算法
2.1 基本演算法
其中d表明的是某個節點第一次被發現的時間點,f表明從節點出發的全部節點已經被發現的時間。
2.2 設計實現
// 深度優先遍曆演算法 public void DepthFirstVisit(GraphVertex v) { // 剛剛被發現,顏色為gray Console.WriteLine(v.Symbol); v.Color = VertexColor.GRAY; this.time++; // 開始時間 v.StartTime = this.time; foreach (GraphEdge edge in v.FollowEdges) { // 還未被發現 if (edge.To.Color == VertexColor.WHITE) { edge.To.Parent = v; DepthFirstVisit(edge.To); } } // 如果邊都已經發現完成 v.Color = VertexColor.BLACK; this.time++; v.FinishTime = this.time; } public void DepthFirstTravel() { // 全域時間變數 this.time = 0; // 初始化 GraphVertex v; foreach (DictionaryEntry e in this.graph) { v = (GraphVertex)e.Value; v.Color = VertexColor.WHITE; v.Parent = null; } // 遞迴調用 // 隊所有的頂點 foreach (DictionaryEntry e in this.graph) { v = (GraphVertex)e.Value; // 頂點為白色 if (v.Color == VertexColor.WHITE) { DepthFirstVisit(v); } }
}
3. 有向圖的遍曆演算法:廣度優先
3.1 基本演算法
其中color域表示的是當前某個節點被發現的狀態。如果是white表明沒有被發現,gray表示當前頂點已經被發現,但是從該節點出發的節點還沒有被全部發現。parent域定義的是在搜尋演算法時父節點。distance域表明的是從節點s到某個發現的節點v的路徑距離。
3.2 設計實現
// 廣度優先遍曆演算法,同時產生廣度優先樹 public void BreadthFirstTravel(GraphVertex s) { // 初始化所有節點 GraphVertex v; foreach (DictionaryEntry e in this.graph) { v = (GraphVertex)e.Value; v.Color = VertexColor.WHITE; v.Distance = int.MaxValue; v.Parent = null; } // 發現第一個節點 s.Color = VertexColor.GRAY; s.Distance = 0; s.Parent = null; // 初始化隊列 Queue context = new Queue(); context.Enqueue(s); // 如果隊列不空的話 while (context.Count != 0) { // 隊首元素出隊 v = context.Dequeue() as GraphVertex; Console.WriteLine(v.Symbol); // 遍曆v的節點 foreach (GraphEdge item in v.FollowEdges) { if ( item.To.Color == VertexColor.WHITE) { item.To.Color = VertexColor.GRAY; item.To.Distance = v.Distance + 1; item.To.Parent = v; context.Enqueue(item.To); } } v.Color = VertexColor.BLACK; }
}
4. 代碼反思
上面的搜尋代碼結構是比較典型的搜尋結構:首先定義隊列或者是棧來儲存程式運行狀態,如果容器不空,取出元素,然後對取出的元素做一些處理。