圖解:深度優先搜索與廣度優先搜索

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/33/332c65978f1fb19aa28c08bd2fe008d0.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖算法第二篇 深度優先搜索與廣度優先搜索及其應用"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"約定:本文所有涉及的圖均爲無向圖,有向圖會在之後的文章涉及"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3c/3c3c44df2259fa652031922e32bf8e71.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1.圖的存儲方式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們首先來回顧一下圖的存儲方式:鄰接矩陣和鄰接表。爲了實現更好的性能,我們在實際應用中一般使用"},{"type":"codeinline","content":[{"type":"text","text":"鄰接表"}]},{"type":"text","text":"的方式來表示圖。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c9/c9a055e4fea85ec5cc88bb87614ca475.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"具體的實現代碼爲:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package Graph;\n\nimport java.util.LinkedList;\n\npublic class Graph{\n private final int V;//頂點數目\n private int E;//邊的數目\n private LinkedList adj[];//鄰接表\n\n public Graph(int V){\n //創建鄰接表\n //將所有鏈表初始化爲空\n this.V=V;this.E=0;\n adj=new LinkedList[V];\n for(int v=0;v();\n }\n }\n\n public int V(){ return V;}//獲取頂點數目\n public int E(){ return E;}//獲取邊的數目\n\n public void addEdge(int v,int w){\n adj[v].add(w);//將w添加到v的鏈表中\n adj[w].add(v);//將v添加到w的鏈表中\n E++;\n }\n\n public Iterable adj(int v){\n //我們不必注意這個細節,可以直接把它忽視而不會影響任何關於圖的理解與實現\n return adj[v];\n }\n\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來,我們會首先介紹"},{"type":"codeinline","content":[{"type":"text","text":"深度優先搜索"}]},{"type":"text","text":"與"},{"type":"codeinline","content":[{"type":"text","text":"廣度優先搜索"}]},{"type":"text","text":"的原理和具體實現;然後根據這兩個基本的模型,我們會介紹六種典型的應用,這些應用只是在深搜和廣搜的代碼的基礎上進行了一些加工,卻可以解決不同的問題!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"注意:我們的思路:如何遍歷一張圖?==>深搜與廣搜==>能夠解決的問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2.深度優先搜索"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"深度優先搜索是利用遞歸方法實現的。我們只需要在訪問其中一個頂點時:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將它標記爲已經訪問"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"遞歸地訪問它的所有沒有被標記過的鄰居頂點"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來仔細地看一下這個過程:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b4/b4f49cd1dbb34db854ac6f88eea13cf2.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0b/0ba011edff5de7d26aa300c5ccb68a1a.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/97/97dc7b9551b1d95ebf0884cced2717d3.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/92/9208a43563b254374edc8895f4e3ba4f.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d1/d1bd19860deb668727406eebdc41d03f.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d2/d25a010194d157ab80ee8c0faada7c8a.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/00/00a1498be15977f9c9e80841377a5677.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/90/90569ebe2eb586197aa4adb135fc9bd0.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/92/92e2e125c8d2b6fa8a12a5a749463ebc.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/47/4783401900d02659ae074d215787e0b9.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0e/0e133a41e80d9eeac1e2d1baf158988b.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5c/5c8e2b7f31cf45ac428abc0d2c6379af.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"深度優先搜素大致可以這樣描述:不撞南牆不回頭,它沿着一條路一直走下去,直到走不動了然後返回上一個岔路口選擇另一條路繼續前進,一直如此,直到走完所有能夠到達的地方!它的名字中"},{"type":"text","marks":[{"type":"strong"}],"text":"深度"},{"type":"text","text":"兩個字其實就說明了一切,每一次遍歷都是走到最深!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意一個細節:在我們上面的最後一個圖中,頂點3仍然未被標記(綠色)。我們可以得到以下結論:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"使用深度優先搜索遍歷一個圖,我們可以遍歷的範圍是所有和起點連通的部分"},{"type":"text","text":",通過這一個結論,下文我們可以實現一個"},{"type":"text","marks":[{"type":"strong"}],"text":"判斷整個圖連通性"},{"type":"text","text":"的方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"深度優先搜索的代碼實現,我是用Java實現的,其中,我定義了一個類,這樣做的目的是更加清晰(畢竟,一會後面還有很多算法)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package Graph;\n\npublic class DepthFirthSearch {\n\n private boolean[] marked;//用來標記頂點\n\n public DepthFirthSearch(Graph G,int s){\n //s是起點\n marked = new boolean[G.V()];\n dfs(G,s);\n }\n\n private void dfs(Graph G,int v){\n marked[v]=true;//標記頂點,這是我們的第一條原則\n \n //對於所有沒有被標記的鄰居頂點,遞歸訪問,這時第二條原則\n for(int w:G.adj(v))\n if(!marked[w]) dfs(G, w);\n }\n\n public boolean marked(int w){\n //判斷一個頂點能否從起點到達;因爲在深搜的過程中,只要被標記了就是能夠到達,反正就是不連通的\n return marked[w];\n }\n\n \n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3.深搜應用(一):查找圖中的路徑"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們通過深度優先搜索可以輕鬆地遍歷一個圖,如果我們在此基礎上增加一些代碼就可以很方便地查找圖中的路徑!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如,題目給定"},{"type":"codeinline","content":[{"type":"text","text":"頂點A"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"頂點B"}]},{"type":"text","text":",讓你求得從"},{"type":"codeinline","content":[{"type":"text","text":"A"}]},{"type":"text","text":"能不能到達"},{"type":"codeinline","content":[{"type":"text","text":"B"}]},{"type":"text","text":"?如果能,給出一個可行的路徑!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了解決這個問題,我們添加了一個實例變量"},{"type":"codeinline","content":[{"type":"text","text":"edgeTo[]"}]},{"type":"text","text":"整型數組來記錄路徑。比如:我們從頂點"},{"type":"codeinline","content":[{"type":"text","text":"A"}]},{"type":"text","text":"直接到達"},{"type":"codeinline","content":[{"type":"text","text":"頂點B"}]},{"type":"text","text":",那麼就令"},{"type":"codeinline","content":[{"type":"text","text":"edgeTo[B]=A"}]},{"type":"text","text":",也就是“**edge to B is A**”,其中"},{"type":"codeinline","content":[{"type":"text","text":"A"}]},{"type":"text","text":"是距離"},{"type":"codeinline","content":[{"type":"text","text":"B"}]},{"type":"text","text":"最近的頂點且從"},{"type":"codeinline","content":[{"type":"text","text":"A"}]},{"type":"text","text":"可以到達"},{"type":"codeinline","content":[{"type":"text","text":"B"}]},{"type":"text","text":"。我舉個簡單的例子:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b5/b59dfc70fd2d0a61b5728076c7a2f380.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"具體的代碼如下,其中爲了實現這個功能,我定義了一個完整的類:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package Graph;\n\nimport java.util.Stack;\n\npublic class DepthFirstPaths {\n\n private boolean [] marked;//記錄是否已經訪問\n private int[] edgeTo;//從起點到一個頂點的已知路徑上的最後一個頂點\n private final int s;//查找的起點\n\n public DepthFirstPaths(Graph G,int s){\n //在圖G中查找,s是起點\n marked = new boolean[G.V()];\n edgeTo = new int[G.V()];\n this.s=s;\n dfs(G,s);//遞歸調用dfs\n }\n\n private void dfs(Graph G,int v){\n //從起點v開始查詢\n marked[v]=true;\n for(int w:G.adj(v)){\n if(!marked[w]){\n edgeTo[w]=v;//w的前一個頂點是v\n dfs(G,w);//既然w沒有被標記,就遞歸地進行dfs遍歷它\n }\n }\n\n }\n\n public boolean hasPathTo(int v){\n //判斷是否有從起點到頂點v的路徑\n //如果頂點v被標記了,就說明它可以到達,否則,就不可以到達\n return marked[v];\n }\n\n //打印路徑\n public void pathTo(int v){\n if(!hasPathTo(v)) System.out.println(\"不存在路徑\");;\n Stack path=new Stack();\n\n for(int x=v;x!=s;x=edgeTo[x]){\n path.push(x);\n }\n path.push(s);\n //打印棧中的元素\n while(path.empty()==false)\n System.out.print(path.pop()+\" \");\n System.out.println();\n }\n\n \n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後一個打印函數"},{"type":"codeinline","content":[{"type":"text","text":"pathTo"}]},{"type":"text","text":"地思想就是通過一個"},{"type":"codeinline","content":[{"type":"text","text":"for"}]},{"type":"text","text":"循環,將路徑壓到一個棧裏,通過棧地先進後出地性質實現反序輸出。理解了上面的"},{"type":"codeinline","content":[{"type":"text","text":"dfs"}]},{"type":"text","text":"地過程就好,這裏可以單獨拿出來去理解。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"4.深搜應用(二):尋找連通分量"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還記得我們上文講到的"},{"type":"codeinline","content":[{"type":"text","text":"dfs"}]},{"type":"text","text":"的一條性質嗎?"},{"type":"text","marks":[{"type":"strong"}],"text":"一個dfs搜索能夠遍歷與起點相連通的所有頂點"},{"type":"text","text":"。我們可以這樣思考:申請一個整型數組"},{"type":"codeinline","content":[{"type":"text","text":"id[0]"}]},{"type":"text","text":"用來將頂點分類——“聯通的頂點的"},{"type":"codeinline","content":[{"type":"text","text":"id"}]},{"type":"text","text":"相同,不連通的頂點的"},{"type":"codeinline","content":[{"type":"text","text":"id"}]},{"type":"text","text":"不同”。首先,我對頂點"},{"type":"codeinline","content":[{"type":"text","text":"adj[0]"}]},{"type":"text","text":"進行"},{"type":"codeinline","content":[{"type":"text","text":"dfs"}]},{"type":"text","text":",把所有能夠遍歷到的頂點的"},{"type":"codeinline","content":[{"type":"text","text":"id"}]},{"type":"text","text":"設置爲"},{"type":"codeinline","content":[{"type":"text","text":"0"}]},{"type":"text","text":",然後把這些頂點都標記;接下來對所有沒有被標記的頂點進行"},{"type":"codeinline","content":[{"type":"text","text":"dfs"}]},{"type":"text","text":",執行同樣的操作,比如將"},{"type":"codeinline","content":[{"type":"text","text":"id"}]},{"type":"text","text":"設爲"},{"type":"codeinline","content":[{"type":"text","text":"1"}]},{"type":"text","text":",這樣依次類推,直到把所有的頂點標記。最後我們我們得到的"},{"type":"codeinline","content":[{"type":"text","text":"id[]"}]},{"type":"text","text":"就可以完整的反映這個圖的連通情況。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/26/26ff0fb045fc625502503bf7106a8415.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們需要判斷兩個頂點之間是否連通,只需要比較它們的"},{"type":"codeinline","content":[{"type":"text","text":"id"}]},{"type":"text","text":"即可;同時,我們還可以根據有多少個不同的"},{"type":"codeinline","content":[{"type":"text","text":"id"}]},{"type":"text","text":"來獲得一個圖中"},{"type":"codeinline","content":[{"type":"text","text":"連通分量"}]},{"type":"text","text":"的個數,一舉兩得!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"具體的代碼實現如下:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package Graph;\n\npublic class CC {\n\n private boolean[] marked;\n private int [] id;//用於記錄連通信息,相當於身份id\n private int count;//用來判斷最終一共有多少個不同的id值\n\n public CC(Graph G){\n marked = new boolean[G.V()];\n id=new int[G.V()];\n \n for(int s=0;s queue = new LinkedList<>();\n marked[v]=true;//標記queue起點\n queue.add(v);//將起點加入隊列\n while(!queue.isEmpty()){\n int t=queue.poll();//從隊列中刪去下一個頂點\n for(int w:G.adj(t)){\n if(!marked(w)){\n //對於每個沒有被標記的相鄰頂點\n marked[w]=true;//標記它\n queue.add(w);//並將它添加到隊列\n }\n }\n }\n\n }\n public boolean marked(int w){\n return marked[w];\n }\n}"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"8.廣搜應用(一):查找最短路徑"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實只要在廣度優先搜索的過程中添加一個整型數組"},{"type":"codeinline","content":[{"type":"text","text":"edgeTo[]"}]},{"type":"text","text":"用來存儲走過的路徑就可以輕鬆實現查找最短路徑,因爲其原理和廣搜中的"},{"type":"codeinline","content":[{"type":"text","text":"edgeTo[]"}]},{"type":"text","text":"完全一致,所以這裏我就不多說了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下是具體的代碼實現:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package Graph;\n\nimport java.util.LinkedList;\nimport java.util.Queue;\nimport java.util.Stack;\n\npublic class BreadthFirstPaths {\n\n private boolean[] marked;\n private int[] edgeTo;//到達該頂點的已知路徑上的最後一個頂點\n private final int s;//起點\n\n public BreadthFirstPaths(Graph G,int s){\n marked = new boolean[G.V()];\n edgeTo = new int[G.V()];\n this.s=s;\n bfs(G,s);\n }\n\n private void bfs(Graph G,int v){\n\n Queue queue = new LinkedList<>();\n marked[v]=true;//標記queue起點\n queue.add(v);//將起點加入隊列\n while(!queue.isEmpty()){\n int t=queue.poll();//從隊列中刪去下一個頂點\n for(int w:G.adj(t)){\n if(!marked(w)){\n edgeTo[w]=t;//保存最短路徑的最後一條邊\n //對於每個沒有被標記的相鄰頂點\n marked[w]=true;//標記它\n queue.add(w);//並將它添加到隊列\n }\n }\n }\n\n }\n public boolean marked(int w){\n return marked[w];\n }\n\n \n public boolean hasPathTo(int v){\n //判斷是否有從起點到頂點v的路徑\n //如果頂點v被標記了,就說明它可以到達,否則,就不可以到達\n return marked[v];\n }\n\n public void pathTo(int v){\n if(!hasPathTo(v)) System.out.println(\"不存在路徑\");;\n Stack path=new Stack();\n\n for(int x=v;x!=s;x=edgeTo[x]){\n path.push(x);\n }\n path.push(s);\n //打印棧中的元素\n while(path.empty()==false)\n System.out.print(path.pop()+\" \");\n System.out.println();\n \n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"9.廣搜應用(二):求任意兩頂點間最小距離"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"設想這樣一個問題:給定圖中任意兩點(u,v),求解它們之間間隔的最小邊數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們的想法是這樣的:以其中一個頂點(比如u)爲起點,執行"},{"type":"codeinline","content":[{"type":"text","text":"bfs"}]},{"type":"text","text":",同時申請一個整型數組"},{"type":"codeinline","content":[{"type":"text","text":"distance[]"}]},{"type":"text","text":"用來記錄"},{"type":"codeinline","content":[{"type":"text","text":"bfs"}]},{"type":"text","text":"遍歷到的每一個頂點到起點u的最小距離。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"關鍵:假設在"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"bfs"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"期間,頂點x從隊列中彈出,並且此時我們會將所有相鄰的未訪問頂點i{i1,i2……}推回到隊列中,同時我們應該更新"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"distance [i] = distance [x] + 1;"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"。它們之間的距離差爲1。我們只需要在每一次執行上述進出隊列的時候執行這個遞推關係式,就能保證"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"distance[]"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"中記錄的距離值是正確的!!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請務必仔細思考這個過程,我們很高興只要保證這一點,就可以達到我們計算最短距離的目的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下是我們的代碼實現:"}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package Graph;\n\nimport java.util.LinkedList;\nimport java.util.Queue;\n\npublic class getDistance {\n\n private boolean[] marked;\n private int [] distance;//用來記錄各個頂點到起點的距離\n private final int s;//起點\n\n public getDistance(Graph G,int s){\n marked = new boolean[G.V()];\n distance= new int[G.V()];\n this.s=s;\n bfs(G,s);\n }\n\n private void bfs(Graph G,int v){\n\n Queue queue = new LinkedList<>();\n marked[v]=true;//標記queue起點\n queue.add(v);//將起點加入隊列\n while(!queue.isEmpty()){\n int t=queue.poll();//從隊列中刪去下一個頂點\n for(int w:G.adj(t)){\n if(!marked(w)){\n //對於每個沒有被標記的相鄰頂點\n marked[w]=true;//標記它\n queue.add(w);//並將它添加到隊列\n distance[w]=distance[t]+1;//這裏就是需要添加遞推關係的地方!\n }\n }\n }\n\n }\n public boolean marked(int w){\n return marked[w];\n \n //打印,對於一個給定的頂點,我們可以獲得距離它特定長度的頂點\n public void PrintVertexOfDistance(Graph G,int x){\n for(int i=0;i
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章