深度優先搜索(縮寫DFS)有點相似廣度優先搜索,也是對一個連通圖進行遍歷的算法。它的思想是從一個頂點V0開始,沿着一條路一直走到底,若是發現不能到達目標解,那就返回到上一個節點,而後從另外一條路開始走到底,這種儘可能往深處走的概念便是深度優先的概念。算法
你能夠跳過第二節先看第三節,:)數組
仍是引用上篇文章的樣例圖,起點仍然是V0,咱們修改一下題目意思,只須要讓你找出一條V0到V6的道路,而無需最短路。網絡
圖2-1 尋找V0到V6的一條路(無需最短路徑)app
假設按照如下的順序來搜索:函數
1.V0->V1->V4,此時到底盡頭,仍然到不了V6,因而原路返回到V1去搜索其餘路徑;spa
2.返回到V1後既搜索V2,因而搜索路徑是V0->V1->V2->V6,,找到目標節點,返回有解。.net
這樣搜索只是2步就到達了,可是若是用BFS的話就須要多幾步。blog
(你能夠跳過這一節先看第三節,重點在第三節)遞歸
從上一篇《【算法入門】廣度/寬度優先搜索(BFS) 》中知道,咱們搜索一個圖是按照樹的層次來搜索的。隊列
咱們假設一個節點衍生出來的相鄰節點平均的個數是N個,那麼當起點開始搜索的時候,隊列有一個節點,當起點拿出來後,把它相鄰的節點放進去,那麼隊列就有N個節點,當下一層的搜索中再加入元素到隊列的時候,節點數達到了N2,你能夠想一想,一旦N是一個比較大的數的時候,這個樹的層次又比較深,那這個隊列就得須要很大的內存空間了。
因而廣度優先搜索的缺點出來了:在樹的層次較深&子節點數較多的狀況下,消耗內存十分嚴重。廣度優先搜索適用於節點的子節點數量很少,而且樹的層次不會太深的狀況。
那麼深度優先就能夠克服這個缺點,由於每次搜的過程,每一層只需維護一個節點。但回過頭想一想,廣度優先可以找到最短路徑,那深度優先可否找到呢?深度優先的方法是一條路走到黑,那顯然沒法知道這條路是否是最短的,因此你還得繼續走別的路去判斷是不是最短路?
因而深度優先搜索的缺點也出來了:難以尋找最優解,僅僅只能尋找有解。其優勢就是內存消耗小,克服了剛剛說的廣度優先搜索的缺點。
給出如圖3-1所示的圖,求圖中的V0出發,是否存在一條路徑長度爲4的搜索路徑。
圖3-1
顯然,咱們知道是有這樣一個解的:V0->V3->V5->V6。
這裏先給出上邊處理過程的對應僞代碼。
[cpp] view plain copy
此後堆棧調用返回到V0那一層,由於V1那一層也找不到跟V1的相鄰未訪問節點
此後堆棧調用返回到V3那一層
此後堆棧調用返回到主函數調用DFS(V0,0)的地方,由於已經找到解,無需再從別的節點去搜別的路徑了。
這裏先給出DFS的核心代碼。
[cpp] view plain copy
固然了,這裏的visit數組不必定是必須的,在一會我給出的24點例子中,咱們能夠看到這點,這裏visit的存在只是爲了保證記錄節點不被從新訪問,也能夠有其餘方式來表達的,這裏只給出核心思想。
深度優先搜索的算法須要你對遞歸有必定的認識,重要的思想就是:抽象!
能夠從DFS函數裏邊看到,DFS裏邊永遠只處理當前狀態節點n,而不去關注它的下一個狀態。
它經過把DFS方法抽象,整個邏輯就變得十分的清晰,這就是遞歸之美。
想必你們都玩過一個遊戲,叫作「24點」:給出4個整數,要求用加減乘除4個運算使其運算結果變成24,4個數字要不重複的用到計算中。
例如給出4個數:一、二、三、4。我能夠用如下運算獲得結果24:
1*2*3*4 = 24;2*3*4/1 = 24;(1+2+3)*4=24;……
如上,是有不少種組合方式使得他們變成24的,固然也有沒法獲得結果的4個數,例如:一、一、一、1。
如今我給你這樣4個數,你能告訴我它們可以經過必定的運算組合以後變成24嗎?這裏我給出約束:數字之間的除法中不得出現小數,例如本來咱們能夠1/4=0.25,可是這裏的約束指定了這樣操做是不合法的。
這裏爲了方便敘述,我假設如今只有3個數,只容許加法減法運算。我繪製瞭如圖5-1的搜索樹。
圖5-1
此處只有3個數而且只有加減法,因此第二層的節點最多就6個,若是是給你4個數而且有加減乘除,那麼第二層的節點就會比較多了,當延伸到第三層的時候節點數就比較多了,使用BFS的缺點就暴露了,須要很大的空間去維護那個隊列。而你看這個搜索樹,其實第一層是3個數,到了第二層就變成2個數了,也就是遞歸深度其實不會超過3層,因此採用DFS來作會更合理,平均效率要比BFS快(我沒寫代碼驗證過,讀者自行驗證)。
題目分類來自網絡:
sicily:1019 1024 1034 1050 1052 1153 1171 1187
pku:1088 1176 1321 1416 1564 1753 2492 3083 3411
DFS適合此類題目:給定初始狀態跟目標狀態,要求判斷從初始狀態到目標狀態是否有解。
不知道你注意到沒,在深度/廣度搜索的過程當中,其實相鄰節點的加入若是是有必定策略的話,對算法的效率是有很大影響的,你能夠作一下簡單馬周遊跟馬周遊這兩個題,你就有所體會,你會發現你在搜索的過程當中,用必定策略去訪問相鄰節點會提高很大的效率。
這些運用到的貪心的思想,你能夠再看看啓發式搜索的算法,例如A*算法等。