搜索---广度优先遍历、深度优先遍历、回溯法
參考文章:https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E6%90%9C%E7%B4%A2.md
廣度優(yōu)先搜索(BFS)
廣度優(yōu)先搜索是按層來處理頂點的,距離開始點最近的那些頂點首先被訪問,而最遠的那些頂點則最后被訪問。BFS的代碼使用了一個隊列。搜索的步驟:
我們用一道LeedCode上面的題目講解,題目位置:
https://leetcode-cn.com/problems/shortest-path-in-binary-matrix/
這里我們需要注意三點:
深度優(yōu)先遍歷(DFS)
廣度優(yōu)先搜索一層一層遍歷,每一層得到的所有新節(jié)點,要用隊列存儲起來以備下一層遍歷的時候再遍歷。
而深度優(yōu)先搜索在得到一個新節(jié)點時立即對新節(jié)點進行遍歷:從節(jié)點 0 出發(fā)開始遍歷,得到到新節(jié)點 6 時,立馬對新節(jié)點 6 進行遍歷,得到新節(jié)點 4;如此反復(fù)以這種方式遍歷新節(jié)點,直到?jīng)]有新節(jié)點了,此時返回。返回到根節(jié)點 0 的情況是,繼續(xù)對根節(jié)點 0 進行遍歷,得到新節(jié)點 2,然后繼續(xù)以上步驟。
從一個節(jié)點出發(fā),使用 DFS 對一個圖進行遍歷時,能夠遍歷到的節(jié)點都是從初始節(jié)點可達的,DFS 常用來求解這種 可達性 問題。
在程序?qū)崿F(xiàn) DFS 時需要考慮以下問題:
- 棧:用棧來保存當(dāng)前節(jié)點信息,當(dāng)遍歷新節(jié)點返回時能夠繼續(xù)遍歷當(dāng)前節(jié)點。可以使用遞歸棧。
- 標記:和 BFS 一樣同樣需要對已經(jīng)遍歷過的節(jié)點進行標記。
- 每個結(jié)點有多少個子樹(也就是一個結(jié)點遍歷完,深度優(yōu)先遍歷時,有幾個可選的結(jié)點可以遍歷,比如上圖的0,可以有1、2、5、6四個結(jié)點可以選擇)
我們用LeedCode上面的題目來講解:
695. 島嶼的最大面積
#include <stdio.h> #include <stdlib.h>int dfs(int **grid,int gridSize,int *gridColSize,int row,int col) {//條件判斷if(row<0 || row>=gridSize || col<0 || col>=gridColSize[0] || grid[row][col]==0){return 0;}//將1置為0,表示已經(jīng)訪問過該結(jié)點grid[row][col]=0;//遍歷所有可以遍歷的情況return 1+dfs(grid,gridSize,gridColSize,row,col+1)+dfs(grid,gridSize,gridColSize,row,col-1)+dfs(grid,gridSize,gridColSize,row+1,col)+dfs(grid,gridSize,gridColSize,row-1,col); }int maxAreaOfIsland(int** grid, int gridSize, int* gridColSize) {int area=0,max=0,i,j;for(i=0;i<gridSize;i++){for(j=0;j<gridColSize[0];j++){area = dfs(grid,gridSize,gridColSize,i,j);max = max>area?max:area;}}return max; }int main(void) {int grid[][13] = {{0,0,1,0,0,0,0,1,0,0,0,0,0},{0,0,0,0,0,0,0,1,1,1,0,0,0},{0,1,1,0,1,0,0,0,0,0,0,0,0},{0,1,0,0,1,1,0,0,1,0,1,0,0},{0,1,0,0,1,1,0,0,1,1,1,0,0},{0,0,0,0,0,0,0,0,0,0,1,0,0},{0,0,0,0,0,0,0,1,1,1,0,0,0},{0,0,0,0,0,0,0,1,1,0,0,0,0}};int gridColSize =13;int **p = malloc(sizeof(int)*8);for(int i=0;i<8;i++){p[i]=grid[i];}int max = maxAreaOfIsland(p,8,&gridColSize);printf("max=%d\n",max);return 0;}求島嶼最大面積,也就是從一個結(jié)點出發(fā),我們按前后左右方向走,可以走的最大格子數(shù)(可達最大區(qū)域)。
我們訪問過一個位置后,使用深度優(yōu)先遍歷的話,我們可以有四個選擇,也就是水平或者豎直的四個方向上,這對應(yīng)深度優(yōu)先遍歷,就是每個結(jié)點都有四個子樹。
對于可以訪問的結(jié)點,訪問過后,我們將其值1置為0,表示已經(jīng)訪問過了(0在這個題目當(dāng)中不需要訪問)。
對于棧,這題我們用遞歸,因為有四個選擇,我們在遞歸時,需要加上四個dfs函數(shù)。
結(jié)果:
回溯法
Backtracking(回溯)屬于 DFS。
- 普通 DFS 主要用在 可達性問題 ,這種問題只需要執(zhí)行到特點的位置然后返回即可。
- 而 Backtracking 主要用于求解 排列組合 問題,例如有 { ‘a(chǎn)’,‘b’,‘c’ } 三個字符,求解所有由這三個字符排列得到的字符串,這種問題在執(zhí)行到特定的位置返回之后還會繼續(xù)執(zhí)行求解過程。
因為 Backtracking 不是立即返回,而要繼續(xù)求解,因此在程序?qū)崿F(xiàn)時,需要注意對元素的標記問題:
- 在訪問一個新元素進入新的遞歸調(diào)用時,需要將新元素標記為已經(jīng)訪問,這樣才能在繼續(xù)遞歸調(diào)用時不用重復(fù)訪問該元素;
- 但是在遞歸返回時,需要將元素標記為未訪問,因為只需要保證在一個遞歸鏈中不同時訪問一個元素,可以訪問已經(jīng)訪問過但是不在當(dāng)前遞歸鏈中的元素。
總結(jié)
以上是生活随笔為你收集整理的搜索---广度优先遍历、深度优先遍历、回溯法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。