BFS和DFS詳解以及java實現
前言
圖在演算法世界中的重要地位是不言而喻的,曾經看到一篇Google的工程師寫的一篇《Get that job at Google!》文章中說到面試官問的問題中幾乎有一半的問題都可以用圖的方法去解決。由此也可以看出圖確實適用範圍確實很廣。
圖的表示
閒話不多說,首先要介紹的就是圖的表示,圖最常用的兩種表示方法是鄰接表和鄰接矩陣。顧名思義,這兩種辦法分別用表和矩陣的方式描述圖中各頂點之間的聯絡
下圖展示了兩種表示上面這個圖的方法
BFS
本文將著重介紹遍歷圖的兩種最常用的方法,分別為廣度優先遍歷和深度優先遍歷,後面會具體介紹為什麼這麼命名。首先來看廣度優先遍歷BFS(Breadth First Search),其主要思想是從起始點開始,將其鄰近的所有頂點都加到一個佇列(FIFO)中去,然後標記下這些頂點離起始頂點的距離為1.最後將起始頂點標記為已訪問,今後就不會再訪問。然後再從佇列中取出最先進隊的頂點A,也取出其周邊鄰近節點,加入佇列末尾,將這些頂點的距離相對A再加1,最後離開這個頂點A。依次下去,直到佇列為空為止。從上面描述的過程我們知道每個頂點被訪問的次數最多一次(已訪問的節點不會再訪問),而對於連通圖來說,每個頂點都會被訪問。加上每個頂點的鄰接連結串列都會被遍歷,因此BFS的時間複雜度是Θ(V+E),其中V是頂點個數,E是邊數,也就是所有鄰接表中的元素個數。為了更好的說明這個過程,下圖列出了對一個圖的BFS的過程
private static void bfs(HashMap<Character, LinkedList<Character>> graph,HashMap<Character, Integer> dist,char start) { Queue<Character> q=new LinkedList<>(); q.add(start);//將s作為起始頂點加入佇列 dist.put(start, 0); int i=0; while(!q.isEmpty()) { char top=q.poll();//取出隊首元素 i++; System.out.println("The "+i+"th element:"+top+" Distance from s is:"+dist.get(top)); int d=dist.get(top)+1;//得出其周邊還未被訪問的節點的距離 for (Character c : graph.get(top)) { if(!dist.containsKey(c))//如果dist中還沒有該元素說明還沒有被訪問 { dist.put(c, d); q.add(c); } } } }
執行結果:
從執行結果我們也可以看到,w r作為距離為1的頂點先被訪問,x t v其後,最後訪問y u。上面的程式碼使用了一個小的trick,用dist這個hash表來記錄每個頂點離s的距離,如果dist中沒有這個元素則說明還未被訪問,這時將距離寫入dist中。BFS訪問得到的每個節點與起始頂點的距離是起始頂點到達該頂點的最短距離。從感性認識上來說,BFS向外擴散的方式得到的距離就是最短距離。詳細的證明過程請參考CLRS上的相應章節
DFS
DFS(Depth First Search)深度優先搜尋是從起始頂點開始,遞迴訪問其所有鄰近節點,比如A節點是其第一個鄰近節點,而B節點又是A的一個鄰近節點,則DFS訪問A節點後再訪問B節點,如果B節點有未訪問的鄰近節點的話將繼續訪問其鄰近節點,否則繼續訪問A的未訪問鄰近節點,當所有從A節點出去的路徑都訪問完之後,繼續遞迴訪問除A以外未被訪問的鄰近節點。因為是遞迴過程,所以我們用過程圖看一下也許會更直觀一些。
如下是DFS的程式碼及執行結果
private static void dfs(HashMap<Character , LinkedList<Character>> graph,HashMap<Character, Boolean> visited) { visit(graph, visited, 'u');//為了和圖中的順序一樣,我認為控制了DFS先訪問u節點 visit(graph,visited,'w'); } private static void visit(HashMap<Character , LinkedList<Character>> graph,HashMap<Character, Boolean> visited,char start) { if(!visited.containsKey(start)) { count++; System.out.println("The time into element "+start+":"+count);//記錄進入該節點的時間 visited.put(start, true); for (char c : graph.get(start)) { if(!visited.containsKey(c)) { visit(graph,visited,c);//遞迴訪問其鄰近節點 } } count++; System.out.println("The time out element "+start+":"+count);//記錄離開該節點的時間 } }
執行結果:
我們通過一個全域性變數count記錄了進入每個節點和離開每個節點的時間,我們也可以看到進出元素的時間和過程圖中的訪問過程是一樣的。
總結
總的來說,BFS多用於尋找最短路徑的問題,DFS多用於快速發現底部節點。以後若有時間再貼幾道相關的題目上來。
相關推薦
BFS和DFS詳解以及java實現(轉載)
作者: Leo-Yang 原文都先發布在作者個人部落格: http://www.leoyang.net/ 本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利. 前言
BFS和DFS詳解以及java實現
前言 圖在演算法世界中的重要地位是不言而喻的,曾經看到一篇Google的工程師寫的一篇《Get that job at Google!》文章中說到面試官問的問題中幾乎有一半的問題都可以用圖的方法去解決。由此也可以看出圖確實適用範圍確實很廣。 圖的表示 閒話不多說,首先要
基數排序詳解以及java實現
前言 基數排序(radix sort)又稱桶排序(bucket sort),相對於常見的比較排序,基數排序是一種分配式排序,即通過將所有數字分配到應在的位置最後再覆蓋到原陣列完成排序的過程。我在上一篇講到的計數排序也屬於這種排序模式,上一篇結尾處提到了計數排序的穩定性,即排序前和排序後相同的數字相對位置保持
資料結構----BFS和DFS詳解
前言 The art of teaching is the art of assisting discovery. Name:Willam Time:2017/2/28 這篇部落格將會介紹兩種遍歷圖的演算法,一種是:DFS—-深度優先搜尋,另外一種就是:
設計模式(建立型):Java常用23種設計模式之單例模式詳解以及Java程式碼實現
可以說單例模式是所有設計模式中最簡單的一種。 單例模式就是說系統中對於某類的只能有一個物件,不可能出來第二個。 單例模式也是23中設計模式中在面試時少數幾個會要求寫程式碼的模式之一。主要考察的是多執行緒下面單例模式的執行緒安全性問題。 1.多執行緒安全單例模式例項一(不使用同步鎖)
求無向連通圖的最小割點詳解以及java原始碼實現
import java.util.*; /**尋找割點*/ public class FindArt { static class Node { Node(String name) { this.name=name; Childen=new Arra
Hadoop檔案儲存系統-HDFS詳解以及java程式設計實現
前言 這是關於Hadoop的系列文章。 背景 我們在本系列的第一篇文章的時候就談到過,面對海量資料,我們最為缺乏的就是對大資料量的儲存能力以及處理能力。而這兩種能力在Hadoop的體現分別就是HDFS以及map-redu
確定有限狀態機和非確定有限狀態機詳解 包含Java實現原始碼(Nondeterministic finite automata)
本文將講解確定有限自動狀態機和非確定有限自動狀態機的特點和區別。將結合圖片例子重點講解什麼是非確定有限自動狀態機。最後講解如何將非確定狀態機轉換為確定的狀態機。多圖預警!! 有限自動狀態機可以分為確定的和不確定的。“確定性”將在下文詳講。“有限”性表示存在一個
[從今天開始修煉資料結構]圖的最短路徑 —— 迪傑斯特拉演算法和弗洛伊德演算法的詳解與Java實現
在網圖和非網圖中,最短路徑的含義不同。非網圖中邊上沒有權值,所謂的最短路徑,其實就是兩頂點之間經過的邊數最少的路徑;而對於網圖來說,最短路徑,是指兩頂點之間經過的邊上權值之和最少的路徑,我們稱路徑上第一個頂點是源點,最後一個頂點是終點。 我們講解兩種求最短路徑的演算法。第一種,從某個源點到其餘各頂點的最短路徑
希爾排序超詳解及其java實現
希爾排序雖然已經十分古老了,但其思想確實十分值得我們學習,非常的巧妙 雖然很多資料說希爾排序是叫shell的人提出來的,我個人卻十分好奇,shell的英文好意為殼的意思,感覺希爾排序的思想就像一層層的殼一樣,從內到外越來越大,當然這是我胡謅的,具體原理如下(用的一個例項進行說明的): 原理:
【資料結構與演算法】之排序全家桶(十大排序詳解及其Java實現)---第七篇
本篇文章彙總了10種場常見的排序演算法,篇幅較長,可以通過下面的索引目錄進行定位查閱: 7、桶排序 一、排序的基本概念 1、排序的定義 排序:就是使一串記錄,按照其中的某個或者某些關鍵字的大小,遞增或遞減的排列起來
微博爬蟲“免登入”技巧詳解及 Java 實現
一、微博一定要登入才能抓取? 目前,對於微博的爬蟲,大部分是基於模擬微博賬號登入的方式實現的,這種方式如果真的運營起來,實際上是一件非常頭疼痛苦的事,你可能每天都過得提心吊膽,生怕新浪爸爸把你的那些賬號給封了,而且現在隨著實名制的落地,獲得賬號的渠道估計也會變得越
線索二叉樹詳解以及程式碼實現
參照《大話資料結構》188到194頁。 一、二叉樹的線索儲存結構定義 /* 二叉樹線索儲存結構定義 Link = 0,代表指向左右孩子的指標 Thread= 1 代表指向前驅或後繼的線索*/ typedef enum{ Link, Thread} Pointe
遺傳演算法詳解及Java實現
遺傳演算法的起源 ========== 20世紀60年代中期,美國密西根大學的John Holland提出了位串編碼技術,這種編碼既適合於變異又適合雜交操作,並且他強調將雜交作為主要的遺傳操作。遺傳演算法的通用編碼技術及簡單有效的遺傳操作為其廣泛的應用和成功
GF(p)上的ELGamal型橢圓曲線密碼詳解(Java實現)
出版 his display 集合 避免 其中 密鑰 整數 參考文獻 GitHub 橢圓曲線密碼 橢圓曲線密碼(Elliptic Curve Cryptosystem),簡稱ECC,是Neal Koblitz和Victor Miller於1985年提出的。 研究發
插入排序詳解(Java實現)
一、基本思想 插入排序(Insertion-Sort)的演算法描述是一種簡單直觀的排序演算法。它的工作原理是通過構建有序序列,對於未排序資料,在已排序序列中從後向前掃描,找到相應位置並插入。 二、演算法描述 1.從第一個元素開始,該元素可以認為已經被
KMP演算法詳解 以及程式碼實現
KMP演算法求解什麼型別問題 字串匹配。給你兩個字串,尋找其中一個字串是否包含另一個字串,如果包含,返回包含的起始位置。 如下面兩個字串: char *str = "bacbababadababacambabacaddababacasdsd"; char *ptr = "
並查集(不相交集合)詳解與java實現
目錄 認識並查集 並查集解析 基本思想 如何檢視a,b是否在一個集合? a,b合併,究竟是a的祖先合併在b的祖先上,還是b的祖先合併在a上? 其他路徑壓縮?
【小家java】BlockingQueue阻塞佇列詳解以及5大實現(ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue...)
相關閱讀 【小家java】java5新特性(簡述十大新特性) 重要一躍 【小家java】java6新特性(簡述十大新特性) 雞肋升級 【小家java】java7新特性(簡述八大新特性) 不溫不火 【小家java】java8新特性(簡述十大新特性) 飽受讚譽 【小家java】java9
Ant詳解(用Ant實現Java專案的自動構建和部署)
Ant是一個Apache基金會下的跨平臺的構件工具,它可以實現專案的自動構建和部署等功能。在本文中,主要讓讀者熟悉怎樣將Ant應用到Java專案中,讓它簡化構建和部署操作。 一.安裝與配置 下載地址:http://ant.apache.org/,在本文