02.27 C++編程筆記:DFS 深度優先搜索的基本思想,含實例講解

採用搜索算法解決問題時,需要構造一個表明狀態特徵和不同狀態之間關係的數據結構,這種數據結構稱為結點。不同的問題需要用不同的數據結構描述。

根據搜索問題所給定的條件,從一個結點出發,可以生成一個或多個新的結點,這個過程通常稱為擴展。結點之間的關係一般可以表示成一棵樹,它被稱為解答樹。搜索算法的搜索過程實際上就是根據初始條件和擴展規則構造一棵解答樹並尋找符合目標狀態的結點的過程。

C/C++編程筆記:DFS 深度優先搜索的基本思想,含實例講解


深度優先搜索DFS(Depth First Search)是從初始結點開始擴展,擴展順序總是先擴展最新產生的結點。這就使得搜索沿著狀態空間某條單一的路徑進行下去,直到最後的結點不能產生新結點或者找到目標結點為止。當搜索到不能產生新的結點的時候,就沿著結點產生順序的反方向尋找可以產生新結點的結點,並擴展它,形成另一條搜索路徑。

為了便於進行搜索,要設置一個表存儲所有的結點。由於在深度優先搜索算法中,要滿足先生成的結點後擴展的原則,所以存儲結點的表一般採用棧這種數據結構。

深度優先搜索算法的搜索步驟一般是:

(1)從初始結點開始,將待擴展結點依次放到棧中。

(2)如果棧空,即所有待擴展結點已全部擴展完畢,則問題無解,退出。

(3)取棧中最新加入的結點,即棧頂結點出棧,並用相應的擴展原則擴展出所有的子結點,並按順序將這些結點放入棧中。若沒有子結點產生,則轉(2)。

(4)如果某個子結點為目標結點,則找到問題的解(這不一定是最優解),結束。如果要求得問題的最優解,或者所有解,則轉(2),繼續搜索新的目標結點。

深度優先搜索算法的框架一般為:

void DFS()

{

棧S初始化;

初始結點入棧;

置搜索成功標誌flag= false;

while (棧不為空 && !flag)

{

棧頂元素出棧,賦給current;

while (current 還可以擴展)

{

由結點current擴展出新結點new;

if (new 重複於已有的結點狀態) continue;

new結點入棧;

if (new結點是目標狀態)

{

置flag= true; break;

}

}

}

if (flag) 輸出結果;

else 輸出無解信息;

}

由於深度優先搜索是一個遞歸的過程,因此通常也使用遞歸函數來實現。一般框架為:

void DFS(結點類型 current) // 從結點current出發遞歸地深度優先搜索

{

置visited[current]=true; // 表示結點current已被處理

if (current結點是目標狀態)

{

置搜索成功標誌flag= false;

return ;

}

while (current 還可以擴展)

{

由current結點擴展出新結點new;

if (! visited[new]) DFS(new); // 對未處理的結點new遞歸調用DFS

}

置visited[current]=flase; // 表示結點current以後可能被處理

}

深度優先搜索中擴展結點的原則是先產生的後擴展。因此,深度優先搜索第一個找到的解,並不一定是問題的最優解,要搜索完整個狀態空間,才能確定哪個解是最優解。

【例1】黑色方塊

有一個寬為W、高為H的矩形平面,用黑色和紅色兩種顏色的方磚鋪滿。一個小朋友站在一塊黑色方塊上開始移動,規定移動方向有上、下、左、右四種,且只能在黑色方塊上移動(即不能移到紅色方塊上)。編寫一個程序,計算小朋友從起點出發可到達的所有黑色方磚的塊數(包括起點)。

例如,如圖1所示的矩形平面中,“#”表示紅色磚塊,“.”表示黑色磚塊,“@”表示小朋友的起點,則小朋友能走到的黑色方磚有28塊。

C/C++編程筆記:DFS 深度優先搜索的基本思想,含實例講解


(1)編程思路

採用非遞歸的深度優先搜索法解決這個問題。

用數組s模擬棧操作,棧頂指針為top,初始時,top=-1,表示棧空。

入棧操作為 s[++top]=t;

出棧操作為 t=s[top--] 。

程序中定義方磚的位置座標(x,y)為Node類型,定義數組int

visit[N][N]標記某方磚是否已走過,visit[i][j]=0表示座標(i,j)處的方磚未走過,visit[i][j]=1表示座標(i,j)處的方磚已走過。初始時visit數組的所有元素值均為0。

具體算法步驟為:

① 將出發點(startx,starty)入棧s,且置visit[startx][starty]=1,表示該處的方磚已被處理,以後不再重複搜索。

② 將棧s的棧頂元素出棧,得到一個當前方磚cur,黑色方磚計數(sum++),沿其上、下、左、右四個方向上搜索未走過的黑色方磚,將找到的黑色方磚的座標入棧。

③ 重複執行②,直至棧s為空,則求出了所有能走過的黑色方磚數。

(2)源程序及運行結果

#include <iostream>

using namespace std;

#define N 21

struct Node

{

int x;

int y;

};

int dx[4]={-1,1,0,0};

int dy[4]={0,0,-1,1};

char map[N][N];

int visit[N][N];

int dfs(int startx, int starty,int w,int h)

{

Node s[N*N],cur,next; // s為棧

int top,i,x,y,sum; // top為棧頂指針

top=-1; // 棧S初始化

sum=0;

cur.x=startx; cur.y=starty;

visit[startx][starty]=1;

s[++top]=cur; // 初始結點入棧;

while(top>=0) // 棧不為空

{

cur=s[top--]; // 棧頂元素出棧

sum++; // 方磚計數

for (i=0;i<4;i++)

{

x=cur.x+dx[i]; y=cur.y+dy[i];

if(x >=0 &&

x=0 && y

&& visit[x][y]==0)

{

visit[x][y] = 1;

next.x=x; next.y=y; // 由cur擴展出新結點next

s[++top]=next; // next結點入棧

}

}

}

return sum;

}

int main()

{

int i,j,pos_x,pos_y,w,h,sum;

while(1)

{

cin>>w>>h;

if (w==0 && h==0) break;

for(i=0;i

{

for(j=0;j

{

cin>>map[i][j];

if (map[i][j]=='@')

{

pos_x = i; pos_y = j;

}

visit[i][j] = 0;

}

}

sum=dfs(pos_x, pos_y,w,h);

cout<

}

return 0;

}

編譯並執行以上程序,可得到如下所示的結果。

8 5

......#.

..##..@#

...#....

#......#

.#....#.

28

0 0

也可以採用遞歸的方法編寫程序。

(3)深度優先搜索採用遞歸函數實現的源程序

#include <iostream>

using namespace std;

#define N 21

char map[N][N];

int visit[N][N];

int w,h,sum;

void dfs(int x, int y)

{

if(x >=0 && x=0 && y

{

visit[x][y] = 1;

sum++;

dfs(x+1,y); // 遞歸訪問四個方向的磚塊

dfs(x-1,y);

dfs(x,y+1);

dfs(x,y-1);

}

}

int main()

{

int i,j,pos_x,pos_y;

while(1)

{

cin>>w>>h;

if (w==0 && h==0) break;

for(i=0;i

{

for(j=0;j

{

cin>>map[i][j];

if (map[i][j]=='@')

{

pos_x = i; pos_y = j;

}

visit[i][j] = 0;

}

}

sum = 0;

dfs(pos_x, pos_y);

cout<

}

return 0;

}

——————————————————————

想要在程序員生涯內有更高的成就的話,最最重要的是儘可能的提升自己的編程能力,並且,與其想著怎麼去提升,不如從現在開始動手動腦,如果對於C/C++感興趣的話,可以關注+私信小編【編程交流】有一些視頻希望可以幫助到你,學習不怕從零開始,就怕從不開始。

C/C++編程筆記:DFS 深度優先搜索的基本思想,含實例講解


"


分享到:


相關文章: