炒雞詳細的深度優先搜尋,dfs其實很簡單!

2021-10-07 04:50:28 字數 3980 閱讀 2636

dfs好久之前就想寫一篇了,終於找到藉口了 為了老弟來寫一篇深度優先搜尋吧!

深度優先搜尋(deepth first search)又叫dfs,基本思想是從給定點出發,選定一條路就一直走下去,不撞南牆不回頭那種。不管你喜不喜歡,它就是這樣專一。

一直走到頭撞了南牆發現當前路徑不可行的時候,才回頭這時候的回頭是通過+回溯來實現的,那麼具體怎麼搞↓

dfs應用有很多,最基本的就是迷宮求路徑數的問題。比如我們選擇如下的簡單迷宮↓從a開始走,每次只能上下左右四個方向走一格,直到b終點結束。求所有可以走的路。

首先確認當前位置沒有問題是可以走的。那麼從當前位置出發,選乙個方向開始試探,如果試探的這個方向可以走,就繼續從這個新位置向下選個方向試探……直到撞了南牆就回頭,或者到達迷宮出口為止

是不是很熟悉?對!每到乙個位置我們要做的步驟都是一!樣!的!所以我們可以通過遞迴來實現。

遞迴就必須考慮遞迴出口(也叫基線條件baseline、邊界條件啥啥都行隨你開心)我們的遞迴出口是啥?走到**就可以結束這個函式了?(其實上面已經說了)走到出口!

看**之前,停下來思考三個問題:

什麼叫可以走

如何選擇試探的方向

怎麼回頭

乙個點可以走就是滿足:(1)這個點在這個圖中 (2)這個點是通路(在簡單迷宮中,不是牆壁即可) 實現上用乙個if判斷一下就好

選擇試探的方向,就是下一步應該往哪走有幾種可能的選擇,就要遍歷這些選擇(單個選擇直接加上以後遞迴,多個選擇用for迴圈來遍歷所有)

回頭就是回溯,因為要回頭說明你的這個點已經不可行了,那麼就把你對這個位置做過的事情全部撤銷。如果為了走這個位置,需要把這個位置標記成x,那就還原這個位置原來的狀態。

比如我們的迷宮問題,從乙個點可達的點就是它的上下左右四個位置所以我們遍歷這四個位置。在乙個用二維陣列表示的圖中如何實現?a點(x,y)的上面就是(x-1,y),下面是(x+1, y),左:(x, y-1) 右:(x, y+1)

當然你也可以用四個遞迴的呼叫語句來實現↓ 這裡 g 表示圖

g[x]

[y]=

' ';

//標記通路

dfs(x +

1, y)

;//往下試探

dfs(x -

1, y)

;//往上試探

dfs(x, y +1)

;//往右試探

dfs(x, y -1)

;//往左試探

g[x]

[y]=

'o';

//回溯,撤銷操作

取巧一點的辦法(不想看就用上面那個法子試探,可以跳到**描述了):

設乙個4x2的二維陣列(不理解二維陣列的話用兩個一維也是一樣的,具體方法放在後面說),第一維度有四個量,表示上下左右四個方向,第二維度有兩個量乙個表示x乙個表示y,即往這個方向試探的話,xy怎麼變化↓

int go[4]

[2]=

,,,}

;//上下左右試探四個方向

二維陣列的試探方法如上,每個裡面的的花括號代表乙個方向。比如,我站在(x, y)這個位置如果想要往上走,用(x + go[1][0], y + go[1][1])就能順利得到(x - 1, y)

用這樣乙個陣列是不是太麻煩了?不是,如果你用乙個陣列,上下左右試探這件事情就可以用乙個j = 0~3的for迴圈來實現遍歷。迴圈內部(x + go[j][0], y + go[j][1])

g[x]

[y]=

' ';

//標記通路

for(

int j =

0; j <

4; j++

)//試探四個方向

dfs(x + go[j][0

], y + go[j][1

]); g[x]

[y]=

'o';

//回溯,,撤銷操作

用兩個一位陣列來試探也是一樣的,只是把描述x和y的位置變化分成了兩個陣列而不是放在第二維度而已。文末會提。

寫**之前,再來過一遍思路:

判斷當前這個位置能不能走

能走就標記,然後從這個位置開始上下左右四個方向試探

遞迴出口是走到了終點(當前位置==終點位置)

ps:這裡迷宮的通路用o表示,牆壁用x以及表示圖的二維矩陣g和一些變數我都用了全域性的。不想這麼寫可以放在引數裡面

#include

using namespace std;

const

int n =

505;

//假設地圖大小不超過這個範圍

char g[n]

[n];

int n, tx, ty;

//圖的實際大小,終點座標

int go[4]

[2]=

,,,}

;//上下左右試探四個方向

void

printgraph()

cout<

//printgraph()

void

dfs(

int x,

int y)

if(g[x]

[y]==

'o'&& x >=

0&& y >=

0&& x < n && y < n)

g[x]

[y]=

'o';

//回溯,能執行到這裡說明這個點已經不可行或者可行解已經輸出。還原這個位置的狀態}}

//dfs()

intmain()

8

0 07 7

o******x

ooooo***

xoxxooox

xoxxoxxo

xo******

xoxxooox

xooooxoo

******xo

好了就是這樣了,是不是還蠻簡單的?其實思路就三句話:

判斷當前點可不可行

可行就修改這個點的狀態,然後遞迴試探這個點可達的所有點

試探完成就回溯,還原這個點原始的狀態

如果需要求路徑條數,改一改遞迴出口的操作就可以了。深搜適合求路徑數量(可行解數),而廣搜適合求最短路(最優可行解),下次寫廣搜吧。。

啊,差點忘了兩個一位陣列做試探。就是分別用兩個一位陣列來表示x和y的試探方向。

int dy[4]

=;//控制左右移動

int dx[4]

=;//控制上下移動

注意用兩個一位陣列的時候,對應下標要匹配。即往上走的時候用的是(x + dx[j], y + dy[j])這個j對應的位置在兩個陣列中要匹

深度優先搜尋DFS

作為搜尋演算法的一種,dfs對於尋找乙個解的 np 包括npc 問題作用很大。但是,搜尋演算法畢竟是 時間複雜度是o n 的階乘級演算法,它的效率比較低,在資料規模變大時,這種演算法就顯得力不從心了。關於深度優先搜尋的效率問題,有多種解決方法。最具有通用性的是剪枝 prunning 也就是去除沒有用...

深度優先搜尋 DFS

深度優先搜尋 縮寫dfs 有點類似廣度優先搜尋,也是對乙個連通圖進行遍歷的演算法。它的思想是從乙個頂點v 0開始,沿著一條路一直走到底,如果發現不能到達目標解,那就返回到上乙個節點,然後從另一條路開始走到底,這種盡量往深處走的概念即是深度優先的概念。你可以跳過第二節先看第三節,還是引用上篇文章的樣例...

深度優先搜尋(dfs)

深度優先搜尋的一般步驟 1 從頂點v出發,訪問v。2 找出剛才訪問過的頂點的第乙個未被訪問的鄰接點,訪問該頂點。以該頂點為新頂點,重複此步驟,直到剛訪問的頂點沒有沒有未被訪問過的鄰接點為止。3 返回前乙個訪問過的仍有未被訪問過的鄰接點的頂點,找出該頂點的下乙個未被訪問過的鄰接點,訪問該頂點。4 重複...