本文兼參考自《算法導論》及《算法》。java
之前一直不可以理解深度優先搜索和廣度優先搜索,老是很怕去碰它們,但通過閱讀上邊提到的兩本書,豁然開朗,立刻就能理解得更進一步。git
下文將會用到的一個無向圖例子以下:github
在《算法》這本書中,做者寫了很好的一個故事。這個故事讓我立刻理解了深度優先搜索的思想。算法
以下圖1-1所示,如何在這個迷宮中找到出路呢?方法見圖1-2.ide
圖1-1 等價的迷宮模型this
探索迷宮而不迷路的一種古老辦法(至少能夠追溯到忒修斯和米諾陶的傳說)叫作Tremaux搜索,以下圖所示。要探索迷宮中的全部通道,咱們須要:spa
1)選擇一條沒有標記過的通道,在你走過的路上鋪一條繩子;code
2)標記全部你第一次路過的路口和通道;blog
3)當來到一個標記過的路口時,用繩子回退到上個路口;rem
4)當回退到的路口已沒有可走的通道時繼續回退。
繩子能夠保證你總能找到一條出路,標記則能保證你不會兩次通過同一條通道或同一個路口。
圖1-2 Tremaux探索
《算法》做者給出的Java代碼以下:
1 public class DepthFirstSearch 2 { 3 private boolean[] marked; 4 private int count; 5 6 public DepthFirstSearch(Graph G, int s) 7 { 8 marked = new boolean[G.V()]; 9 dfs(G, s); 10 } 11 12 private void dfs(Graph G, int v) 13 { 14 marked[v] = true; 15 count++; 16 for(int w : G.adj(v)) 17 { 18 if(!marked[w]) 19 { 20 dfs(G, w); 21 } 22 } 23 } 24 25 public boolean marked(int w) 26 { 27 return marked[w]; 28 } 29 30 public int count() 31 { 32 return count; 33 } 34 }
具體例子以下圖1-3:
圖1-3 使用深度優先探索的軌跡,尋找全部和頂點0連通的頂點
《算法》做者給出的Java代碼以下:
1 public class DepthFirstPaths 2 { 3 private boolean[] marked; // Has dfs() been called for this vertex? 4 private int[] edgeTo; // last vertex on known path to this vertex 5 private final int s; // source 6 public DepthFirstPaths(Graph G, int s) 7 { 8 marked = new boolean[G.V()]; 9 edgeTo = new int[G.V()]; 10 this.s = s; 11 dfs(G, s); 12 } 13 14 private void dfs(Graph G, int v) 15 { 16 marked[v] = true; 17 for (int w : G.adj(v)) 18 if (!marked[w]) 19 { 20 edgeTo[w] = v; 21 dfs(G, w); 22 } 23 } 24 25 public boolean hasPathTo(int v) 26 { 27 return marked[v]; 28 } 29 30 public Iterable<Integer> pathTo(int v) 31 { 32 if (!hasPathTo(v)) return null; 33 Stack<Integer> path = new Stack<Integer>(); 34 for (int x = v; x != s; x = edgeTo[x]) 35 path.push(x); 36 path.push(s); 37 return path; 38 } 39 }
圖1-4 pathTo(5)的計算軌跡
圖1-5 使用深度優先搜索的軌跡,尋找全部起點爲0的路徑
深度優先搜索就好像是一我的在走迷宮,廣度優先搜索則好像是一組人在一塊兒朝各個方向走這座迷宮,每一個人都有本身的繩子。當出現新的叉路時,能夠假設一個探索者能夠分裂爲更多的人來搜索它們。當兩個探索者相遇時,會合二爲一(並繼續使用先到達者的繩子)。以下圖2-1:
圖2-1 廣度優先的迷宮搜索
《算法》做者給出的使用廣度優先搜索查找圖中的路徑的Java代碼以下:
1 public class BreadthFirstPaths 2 { 3 private boolean[] marked; // Is a shortest path to this vertex known? 4 private int[] edgeTo; // last vertex on known path to this vertex 5 private final int s; // source 6 7 public BreadthFirstPaths(Graph G, int s) 8 { 9 marked = new boolean[G.V()]; 10 edgeTo = new int[G.V()]; 11 this.s = s; 12 bfs(G, s); 13 } 14 15 private void bfs(Graph G, int s) 16 { 17 Queue<Integer> queue = new Queue<Integer>(); 18 marked[s] = true; // Mark the source 19 queue.enqueue(s); // and put it on the queue. 20 while (!q.isEmpty()) 21 { 22 int v = queue.dequeue(); // Remove next vertex from the queue. 23 for (int w : G.adj(v)) 24 if (!marked[w]) // For every unmarked adjacent vertex, 25 { 26 edgeTo[w] = v; // save last edge on a shortest path, 27 marked[w] = true; // mark it because path is known, 28 queue.enqueue(w); // and add it to the queue. 29 } 30 } 31 } 32 33 public boolean hasPathTo(int v) 34 { 35 return marked[v]; 36 } 37 38 public Iterable<Integer> pathTo(int v) 39 // Same as for DFS. 40 }
一個例子以下:
圖2-2 使用廣度優先搜索尋找全部起點爲0的路徑的結果
圖2-3 使用廣度優先搜索的軌跡,尋找全部起點爲0的路徑
具體代碼已經託管到Github.