1.DFS(深度優先搜索)
搜索思想在圖問題中能以最直觀的方式展現。
深度優先搜索的步驟分爲:
- 遞歸下去。
- 回溯上來。
顧名思義,深度優先,則是以深度爲準則,先一條路走到底,直到達到目標。這裏稱之爲遞歸下去。
否則既沒有達到目標又無路可走了,那麼則退回到上一步的狀態,走其他路。這便是回溯上來。
下面結合具體例子來理解。
如圖所示,在一個迷宮中,黑色塊代表玩家所在位置,紅色塊代表終點,問是否有一條到終點的路徑
我們用深度優先搜索的方法去解這道題,由圖可知,我們可以走上下左右四個方向,我們規定按照左下右上的方向順序走,即,如果左邊可以走,我們先走左邊。然後遞歸下去,沒達到終點,我們再回溯上來,等又回溯到這個位置時,左邊已經走過了,那麼我們就走下邊,按照這樣的順序與方向走。並且我們把走過的路標記一下代表走過,不能再走。
所以我們從黑色起點首先向左走,然後發現還可以向左走,最後我們到達圖示位置
已經連續向左走到左上角的位置了,這時發現左邊不能走了,這時我們就考慮往下走,發現也不能走,同理,上邊也不能走,右邊已經走過了也不能走,這時候無路可走了,代表這條路是死路不能幫我們解決問題,所以現在要回溯上去,回溯到上一個位置
在這個位置我們由上可知走左邊是死路不行,上下是牆壁不能走,而右邊又是走過的路,已經被標記過了,不能走。所以只能再度回溯到上一個位置尋找別的出路。
最終我們回溯到最初的位置,同理,左邊證明是死路不必走了,上和右是牆壁,所以我們走下邊。然後遞歸下去
到了這個格子,因爲按照左下右上的順序,我們走左邊,遞歸下去
一直遞歸下去到最左邊的格子,然後左邊行不通,走下邊。
然後達到目標。DFS的重要點在於狀態回溯。
僞代碼如下:
int goal_x = 9, goal_y = 9; //目標的座標,暫時設置爲右下角
int n = 10 , m = 10; //地圖的寬高,設置爲10 * 10的表格
int graph[n][m]; //地圖
int used[n][m]; //用來標記地圖上那些點是走過的
int px[] = {-1, 0, 1, 0}; //通過px 和 py數組來實現左下右上的移動順序
int py[] = {0, -1, 0, 1};
int flag = 0; //是否能達到終點的標誌
void DFS(int graph[][], int used[], int x, int y)
{
// 如果與目標座標相同,則成功
if (graph[x][y] == graph[goal_x][goal_y]) {
printf("successful");
flag = 1;
return ;
}
// 遍歷四個方向
for (int i = 0; i != 4; ++i) {
//如果沒有走過這個格子
int new_x = x + px[i], new_y = y + py[i];
if (new_x >= 0 && new_x < n && new_y >= 0
&& new_y < m && used[new_x][new_y] == 0 && !flag) {
used[new_x][new_y] = 1; //將該格子設爲走過
DFS(graph, used, new_x, new_y); //遞歸下去
used[new_x][new_y] = 0;//狀態回溯,退回來,將格子設置爲未走過
}
}
}
2.DFS(廣度優先搜索)
廣度優先搜索較之深度優先搜索之不同在於,深度優先搜索旨在不管有多少條岔路,先一條路走到底,不成功就返回上一個路口然後就選擇下一條岔路,而廣度優先搜索旨在面臨一個路口時,把所有的岔路口都記下來,然後選擇其中一個進入,然後將它的分路情況記錄下來,然後再返回來進入另外一個岔路,並重復這樣的操作,用圖形來表示則是這樣的,例子同上
從黑色起點出發,記錄所有的岔路口,並標記爲走一步可以到達的。然後選擇其中一個方向走進去,我們走黑點方塊上面的那個,然後將這個路口可走的方向記錄下來並標記爲2,意味走兩步可以到達的地方。
接下來,我們回到黑色方塊右手邊的1方塊上,並將它能走的方向也記錄下來,同樣標記爲2,因爲也是走兩步便可到達的地方
這樣走一步以及走兩步可以到達的地方都搜索完畢了,下面同理,我們可以迅速把三步的格子給標記出來
再之後是四步,五步。
我們便成功尋找到了路徑,並且把所有可行的路徑都求出來了。在廣度優先搜索中,可以看出是逐步求解的,反覆的進入與退出,將當前的所有可行解都記錄下來,然後逐個去查看。在DFS中我們說關鍵點是遞歸以及回溯,在BFS中,關鍵點則是狀態的選取和標記。
僞代碼如下:
int n = 10, m = 10; //地圖寬高
void BFS()
{
queue que; //用隊列來保存路口
int graph[n][m]; //地圖
int px[] = {-1, 0, 1, 0}; //移動方向的數組
int py[] = {0, -1, 0, 1};
que.push(起點入隊); //將起點入隊
while (!que.empty()) { //只要隊列不爲空
auto temp = que.pop(); //得到隊列中的元素
for (int i = 0; i != 4; ++i) {
if(//可以走) {
//標記當前格子
//將當前狀態入隊列,等待下次提取
}
}
}
}
總結
數據結構上的運用
DFS用遞歸的形式,用到了棧結構,先進後出。
BDS選取狀態用隊列的形式,先進先出。
複雜度
DFS的複雜度與BFS的複雜度大體一致,不同之處在於遍歷的方式與對於問題的解決出發點不同,DFS適合目標明確的任務,而BFS適合大範圍的尋找。
思想
思想上來說都是窮竭列舉所有的情況。