數據結構與算法_深度優先搜索(DFS)與廣度優先搜索(BFS)詳解

1.DFS(深度優先搜索)

搜索思想在圖問題中能以最直觀的方式展現。

深度優先搜索的步驟分爲:

  1. 遞歸下去。
  2. 回溯上來。

顧名思義,深度優先,則是以深度爲準則,先一條路走到底,直到達到目標。這裏稱之爲遞歸下去。

否則既沒有達到目標又無路可走了,那麼則退回到上一步的狀態,走其他路。這便是回溯上來。

下面結合具體例子來理解。

如圖所示,在一個迷宮中,黑色塊代表玩家所在位置,紅色塊代表終點,問是否有一條到終點的路徑

在這裏插入圖片描述

我們用深度優先搜索的方法去解這道題,由圖可知,我們可以走上下左右四個方向,我們規定按照左下右上的方向順序走,即,如果左邊可以走,我們先走左邊。然後遞歸下去,沒達到終點,我們再回溯上來,等又回溯到這個位置時,左邊已經走過了,那麼我們就走下邊,按照這樣的順序與方向走。並且我們把走過的路標記一下代表走過,不能再走。

所以我們從黑色起點首先向左走,然後發現還可以向左走,最後我們到達圖示位置

在這裏插入圖片描述

已經連續向左走到左上角的位置了,這時發現左邊不能走了,這時我們就考慮往下走,發現也不能走,同理,上邊也不能走,右邊已經走過了也不能走,這時候無路可走了,代表這條路是死路不能幫我們解決問題,所以現在要回溯上去,回溯到上一個位置

在這裏插入圖片描述

在這個位置我們由上可知走左邊是死路不行,上下是牆壁不能走,而右邊又是走過的路,已經被標記過了,不能走。所以只能再度回溯到上一個位置尋找別的出路。

在這裏插入圖片描述

最終我們回溯到最初的位置,同理,左邊證明是死路不必走了,上和右是牆壁,所以我們走下邊。然後遞歸下去

在這裏插入圖片描述

到了這個格子,因爲按照左下右上的順序,我們走左邊,遞歸下去

在這裏插入圖片描述

一直遞歸下去到最左邊的格子,然後左邊行不通,走下邊。

在這裏插入圖片描述

然後達到目標。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適合大範圍的尋找。

思想

思想上來說都是窮竭列舉所有的情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章