相信絕大多數人對於深度優先搜索和廣度優先搜索是不會特別陌生的,若是我這樣說彷佛你沒據說過,那若是我說dfs和bfs呢?先不說是否學習過它們,至少它們的大名應該是都是據說過的吧,深度優先搜索(Depth-First-Search)和廣度優先搜索(Breadth-First-Search)同爲搜索(Search)中的兩個大類。顧名思義,他們的做用即是在必定的元素當中選取符合要求的元素,有些狀況下不管你使用dfs或bfs都能得出結果,可是事實上它們兩個本質上是有區別的html
第一講深度優先搜索(Depth-First-Search)ios
深度優先搜索是一種用途很普遍的算法,這種搜索方法使用的是一種比較基本的概念----遞歸,dfs就是基於遞歸而產生的一種嚴格意義上的算法,最開始的時候dfs是一種在開發爬蟲早期使用較多的方法,它的目的是要達到被搜索結構的葉結點。那麼深度優先搜索究竟是如何實現的呢?算法
要能理解dfs,首先理解遞歸是必要的(遞歸連接https://www.cnblogs.com/qj-Network-Box/p/12729230.html),在理解了遞歸後,就能夠來看看這種算法的思想了函數
假如你要搜尋節點①~節點⑩,而後你經過遍歷找到了節點①,那麼接下來你可使用dfs找到接下來的節點學習
找到第一個節點是必須的,而後就能夠接着往下遞歸測試
而後就要一路找到底,即一直找到要回溯的節點網站
若是到了節點⑤的時候又找到了能夠繼續往下遞歸的條件,那麼還能夠繼續往下搜索spa
而後在②處又找到了下面的一個節點3d
到了新的節點又能夠向下搜搜code
雖然如今回溯到了①,可是搜索還差幾步,由於①再往下還有節點
要一直等到這裏纔是dfs最終結束的地方
以上即是dfs的基本思想了,可是呢dfs並非一種一成不變的算法,事實上不一樣的狀況下所寫出來的dfs是不一樣的,可是思路差很少就相似以上
深度優先搜索(Depth-First-Search)的例題
在某個神奇的網站上,你總能夠找到一些有趣的題鞏固你學的知識,題目連接-->https://www.luogu.com.cn/problem/P1162
這一道題算是一道比較基礎的練習題吧(應該),其實這道題在你可以理解dfs的狀況下,並找到思路的狀況下應該是手到擒來的事。
首先咱們來仔細地讀一讀題大概意思就是被1框住的0要改寫成2,我想不少人看到題的第一反應是想着怎麼區別圈裏面和圈外面的0,而後把圈裏面的0改爲2,彷佛這個思路只有一個難點,那就是如何肯定一個0到底處在圈裏仍是圈外,我開始也是這麼想的,而後我想到了一個解決辦法,以下圖
通常的狀況就如左圖,因此我後來的分析都是基於左圖的
而後我就想到了這個斷定方法,也許你也想到了,可是我在實現這種方法的時候我發現了一個很大的瑕疵,假如那個圈不是規則的,並且不只不規則,並且仍是凹多邊形這種思路就會出問題,問題以下圖所示
若是測試點給的是這樣圖形,以前的斷定方法就完全失效了,由於很明顯這個原本是一個在圈外的點,卻會被斷定成圈內的,那到底該如何識別一個0究竟是圈外仍是圈內呢?說實話,我到如今仍是沒想出來,可是不少時候咱們就可使用逆向思惟大法,既然很難斷定是不是圈內的,那就反過來判斷他是否是圈外的不就完了?首先咱們能夠肯定一個0處於方陣邊緣處確定不是圈內的,因此咱們能夠經過這個方法能夠先找到邊緣的零,那麼,與它相鄰的0以及相鄰於與他相鄰的0定然也是在圈外的,那麼這道題dfs的思路也好說了,找到一個圈外邊緣的0,標記一下,而後搜索與他相鄰的0,並標記後繼續搜索,因爲題目要求,咱們每到一點只要向周圍四個方向搜索便可
供上搜索部分的代碼
1 int n,a[35][35]; 2 int x1[4]={0,1,0,-1}; 3 int y1[4]={1,0,-1,0};//四個方向 4 void dfs(int x,int y) 5 { 6 a[x][y]=5; 7 for(int i=0;i<4;i++) 8 { 9 if(x+x1[i]>=0&&x+x1[i]<n&&y+y1[i]>=0&&y+y1[i]<n) 10 { 11 if(a[x+x1[i]][y+y1[i]]==0) dfs(x+x1[i],y+y1[i]);//dfs 12 } 13 } 14 }
可是這道題只搜索這一次確定是不夠的,咱們還須要考慮其餘狀況,好比下圖
這種狀況下有兩塊不在圈內的區域,因此咱們須要不止一次地運行dfs,也就是說咱們在主函數中能夠將他的邊緣「走」一圈以確保每一塊圈外的0均可以被標記到
供上主函數+dfs
#include<iostream> using namespace std; int n,a[35][35]; int x1[4]={0,1,0,-1}; int y1[4]={1,0,-1,0}; void dfs(int x,int y) { a[x][y]=5; for(int i=0;i<4;i++) { if(x+x1[i]>=0&&x+x1[i]<n&&y+y1[i]>=0&&y+y1[i]<n) { if(a[x+x1[i]][y+y1[i]]==0) dfs(x+x1[i],y+y1[i]); } } } int main(){ cin>>n; for(int i=0;i<n;i++) for(int j=0;j<n;j++) { cin>>a[i][j]; } for(int i=0;i<n;i++) for(int j=0;j<n;j++) { if((i==0||i==n-1)&&a[i][j]==0) dfs(i,j); else if(i>0&&(j==0||j==n-1)&&a[i][j]==0) dfs(i,j); } pp(); return 0; }
如今的代碼就能夠實現把全部不在圈裏的0標記成5,在圈裏的不變,不過代碼還差最後一步,由於咱們標記的時候將圈外的0標記成5,因此輸出的時候要將全部是5的地方輸出0,將本來是0的地方輸出2,因此將這種輸出處理一下就完成了
完整代碼
#include<iostream> using namespace std; int n,a[35][35]; int x1[4]={0,1,0,-1}; int y1[4]={1,0,-1,0}; void pp() { for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { if(a[i][j]==5) cout<<0; else if(a[i][j]==1) cout<<1; else if(a[i][j]==0) cout<<2; cout<<" "; } cout<<endl; } } void dfs(int x,int y) { a[x][y]=5; for(int i=0;i<4;i++) { if(x+x1[i]>=0&&x+x1[i]<n&&y+y1[i]>=0&&y+y1[i]<n) { if(a[x+x1[i]][y+y1[i]]==0) dfs(x+x1[i],y+y1[i]); } } } int main(){ cin>>n; for(int i=0;i<n;i++) for(int j=0;j<n;j++) { cin>>a[i][j]; } for(int i=0;i<n;i++) for(int j=0;j<n;j++) { if((i==0||i==n-1)&&a[i][j]==0) dfs(i,j); else if(i>0&&(j==0||j==n-1)&&a[i][j]==0) dfs(i,j); } pp(); return 0; }
本期的內容就到這裏了,若是你以爲對你有幫助,那麼幫忙把贊👍點一下吧,若是還期待我往後的表現,就點個關注➕好了,咱們下期說說bfs,再見!