搜索---廣度優先遍歷、深度優先遍歷、回溯法

參考文章: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

廣度優先搜索(BFS)

廣度優先搜索是按層來處理頂點的,距離開始點最近的那些頂點首先被訪問,而最遠的那些頂點則最后被訪問。BFS的代碼使用了一個隊列。搜索的步驟:

  1. 首先選擇一個頂點作為起始頂點,將起始頂點放入隊列中
  2. 從隊列首部選出一個頂點,將與之相鄰并且沒有被訪問的結點依次加入到隊列的隊尾,然后訪問這些與之相鄰并且沒有被訪問過的結點,將隊列隊首的結點刪去。
  3. 按照步驟2處理隊列中下一個結點,直到找到要找的結點或者隊列中沒有結點結束。
void BFS()
{定義隊列;定義備忘錄,用于記錄已經訪問的位置;判斷邊界條件,是否能直接返回結果的。將起始位置加入到隊列中,同時更新備忘錄。while (隊列不為空) {獲取當前隊列中的元素個數。for (元素個數) {取出一個位置節點。判斷是否到達終點位置。獲取它對應的下一個所有的節點。條件判斷,過濾掉不符合條件的位置。新位置重新加入隊列。}}}

我們用一道LeedCode上面的題目講解,題目位置:

https://leetcode-cn.com/problems/shortest-path-in-binary-matrix/
這里我們需要注意三點:

  1. 需要一個隊列,來記錄下一次訪問的結點,因為該隊列是記錄結點位置的(訪問結點的下標),如果一維數組可以搞定,定義個int queue[QUEUE_SIZE],如果是二維,定義int queue[QUEUW_SIZE][2].
  2. 需要一個備忘錄,記錄已經被訪問的結點,備忘錄用數組表示
  3. 每一層結點數就是可以訪問的結點,這里題目是 8 個方向上的單元格
#include <stdio.h>
#include <string.h>
#include <stdlib.h>#define QUEUE_SIZE 10001
int BFS()
{int grid[3][3] = {{0,0,0},{1,1,0},{1,1,0}};int gridSize=3;int ans=0; /*判斷邊界條件,即結束條件*/if(grid[gridSize-1][gridSize-1]==1 || grid[0][0]==1)return -1;/* 獲取隊列 */if(gridSize==1)return 1;int m=gridSize,n=m*m,front=0,rear=0,pre_rear=0,cnt=1;/* 設置隊列 */ int **cularr = (int **)malloc(sizeof(int)*n);for(int i=0;i<n;i++){cularr[i]=(int *)malloc(sizeof(int)*2);}/* 將首結點入隊 并將其設置已經訪問過了*/cularr[rear][0]=0;cularr[rear++][1]=0;/*將其修改為1  表示這個數據不需要在訪問 */grid[0][0]=1;/* 根據題目要求廣度優先都要有8個結點 */int temp[8][2] = {{-1,-1},{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0}};while(front<rear){pre_rear = rear;/* 遍歷每一層上的結點 */while(front<pre_rear){for(int i=0;i<8;i++){int x = cularr[front][0]+temp[i][0];int y = cularr[front][1]+temp[i][1];//此時是最后一個結點if((m-1==x)&&(m-1==y)){return cnt+1;}//將沒有訪問符合的符合條件的加入隊列中if(x<m && x>=0 && y<m && y>=0 && grid[x][y]==0){cularr[rear][0]=x;cularr[rear++][1]=y;grid[x][y]=1;}}front++;   //獲取下一個隊首元素}cnt++;     //廣度優先遍歷了一層,要加1}return -1;}int main(void)
{int num = BFS();printf("%d\n",num);return 0;
}

深度優先遍歷(DFS)

在這里插入圖片描述

廣度優先搜索一層一層遍歷,每一層得到的所有新節點,要用隊列存儲起來以備下一層遍歷的時候再遍歷。

而深度優先搜索在得到一個新節點時立即對新節點進行遍歷:從節點 0 出發開始遍歷,得到到新節點 6 時,立馬對新節點 6 進行遍歷,得到新節點 4;如此反復以這種方式遍歷新節點,直到沒有新節點了,此時返回。返回到根節點 0 的情況是,繼續對根節點 0 進行遍歷,得到新節點 2,然后繼續以上步驟。

從一個節點出發,使用 DFS 對一個圖進行遍歷時,能夠遍歷到的節點都是從初始節點可達的,DFS 常用來求解這種 可達性 問題。

在程序實現 DFS 時需要考慮以下問題:

  • 棧:用棧來保存當前節點信息,當遍歷新節點返回時能夠繼續遍歷當前節點。可以使用遞歸棧。
  • 標記:和 BFS 一樣同樣需要對已經遍歷過的節點進行標記。
  • 每個結點有多少個子樹(也就是一個結點遍歷完,深度優先遍歷時,有幾個可選的結點可以遍歷,比如上圖的0,可以有1、2、5、6四個結點可以選擇)

我們用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,表示已經訪問過該結點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;}

求島嶼最大面積,也就是從一個結點出發,我們按前后左右方向走,可以走的最大格子數(可達最大區域)。

我們訪問過一個位置后,使用深度優先遍歷的話,我們可以有四個選擇,也就是水平或者豎直的四個方向上,這對應深度優先遍歷,就是每個結點都有四個子樹。
對于可以訪問的結點,訪問過后,我們將其值1置為0,表示已經訪問過了(0在這個題目當中不需要訪問)。
對于棧,這題我們用遞歸,因為有四個選擇,我們在遞歸時,需要加上四個dfs函數。
結果:
在這里插入圖片描述

回溯法

Backtracking(回溯)屬于 DFS。

  • 普通 DFS 主要用在 可達性問題 ,這種問題只需要執行到特點的位置然后返回即可。
  • 而 Backtracking 主要用于求解 排列組合 問題,例如有 { ‘a’,‘b’,‘c’ } 三個字符,求解所有由這三個字符排列得到的字符串,這種問題在執行到特定的位置返回之后還會繼續執行求解過程。

因為 Backtracking 不是立即返回,而要繼續求解,因此在程序實現時,需要注意對元素的標記問題:

  • 在訪問一個新元素進入新的遞歸調用時,需要將新元素標記為已經訪問,這樣才能在繼續遞歸調用時不用重復訪問該元素;
  • 但是在遞歸返回時,需要將元素標記為未訪問,因為只需要保證在一個遞歸鏈中不同時訪問一個元素,可以訪問已經訪問過但是不在當前遞歸鏈中的元素。
def backtrack(路徑, 選擇列表):if 滿足結束條件:result.add(路徑)returnfor 選擇 in 選擇列表:做選擇backtrack(路徑, 選擇列表)撤銷選擇以上三種定義:
1、路徑:也就是已經做出的選擇
2、選擇列表:也就是當前可以做的選擇
3、結束條件:也就是到達決策樹底層,無法再做選擇的條件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/*** 本解答模仿 https://www.cnblogs.com/wuyuegb2312/p/3273337.html 編寫* 本解答以供自己學習提升和分享 MasterXU*/
static char g_ch[10][4] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz",
};
static int g_total[10] ={0,0,3,3,3,3,3,4,3,4}; 
/*** number: 輸入待轉換數字* answer:解空間,存放路徑上每個節點的選擇,由于是遞歸實現 需要保存1條路徑節點即可* index:  當前深度* depth:  最大路徑深度* ans:    返回結果* pathIdx:當前路徑編號*/
void recursive(char *number, int *answer, int index, int depth, char **ans, int *pathIdx)
{//當當前深度等于最大深度時,表示此時DFS遍歷完一條路徑了,需要將這條路徑的元素記錄if(index == depth){//分配存放該路徑上元素的內存ans[*pathIdx] = (char *)malloc((depth+1)*sizeof(char));//獲取該路徑每層的元素,number[i]-'0'表示選的層數,answer[1]表示選該層的元素for(int i=0;i<depth;i++){ans[*pathIdx][i]=g_ch[number[i]-'0'][answer[i]];}ans[*pathIdx][depth]='\0';//下一次記錄下一路徑的元素(*pathIdx)++;//返回表示該路徑已經記錄完畢return;}/*index+1:表示遍歷下一層answer[index+1]++:遍歷下一個兄弟結點 */for(answer[index]=0;answer[index]<g_total[number[index]-'0'];answer[index]++){recursive(number,answer,index+1,depth,ans,pathIdx);}
}/*** Note: The returned array must be malloced, assume caller calls free().*/
char ** letterCombinations(char * digits, int* returnSize){int a[100] = {0};   //記錄深度遍歷每條路徑上結點的位置int depth = strlen(digits); //深度優先遍歷的最大深度int num = (int)pow(4,depth);    //深度優先遍歷的最多路徑個數*returnSize = 0;    //路徑計數if(depth == 0)return NULL;char **ans = (char **)malloc(num*sizeof(char *));recursive(digits,a,0,depth,ans,returnSize);return ans;}int main(void)
{char *digits = "23";int returnSize =0;char **ans = letterCombinations(digits,&returnSize);printf("returnSize = %d\n",returnSize);for(int i =0;i<returnSize;i++){puts(ans[i]);}return 0;
}

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/379634.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379634.shtml
英文地址,請注明出處:http://en.pswp.cn/news/379634.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何更改Visual Studio 2008中類文件引用的默認名稱空間?

在編寫程序的時候&#xff0c;如果某些名稱空間經常用到&#xff0c;每次創建一個文件的時候&#xff0c;都需要手工添加名稱空間&#xff0c;是不是很煩人呢&#xff1f;多說人會回答&#xff1a;是的。如果新建文件的時候就自動加上自己需要的名稱空間該多好啊。&#xff1a;…

Java ClassLoader findLoadedClass()方法與示例

ClassLoader類findLoadedClass()方法 (ClassLoader Class findLoadedClass() method) findLoadedClass() method is available in java.lang package. findLoadedClass()方法在java.lang包中可用。 findLoadedClass() method is used to return the Class with the given binar…

Linux內核設計與實現---內存管理

內存管理1 頁2 區3 獲得頁獲得填充為0的頁釋放頁4 kmalloc()gfp_mask標志kfree()5 vmalloc()6 slab層slab層的設計7 slab分配器的接口8 在棧上的靜態分配9 高端內核的映射永久映射臨時映射10 每個CPU的分配11 新的每個CPU的接口編譯時的每個CPU數據運行時每個CPU數據12 使用每個…

多語言開發 之 通過基頁類及Session 動態響應用戶對語言的選擇

在用戶通過UserLogin.aspx登錄系統時 提供其對語言的選擇選擇后 將所選存入Session 以便登錄系統后的其他頁面進行按語言顯示當然相關頁面需要支持多語言具體信息可參看使用 根據語言環境不同 而顯示不同的 資源本地化 ASP.NET 網頁 App_Code下定義基頁類 BasePage.cs Codeusin…

Java ClassLoader findSystemClass()方法與示例

ClassLoader類findSystemClass()方法 (ClassLoader Class findSystemClass() method) findSystemClass() method is available in java.lang package. findSystemClass()方法在java.lang包中可用。 findSystemClass() method is used to find the class with the given binary …

TFS 鏈接不上

C:\Users\Administrator>net use 會記錄新的網絡連接。 列表是空的。 C:\Users\Administrator>net use \\192.168.1.61\ipc$ wangkun /user:wangkun 命令成功完成。 轉載于:https://www.cnblogs.com/rhythmK/archive/2012/06/04/2534066.html

Linux內核設計與實現---虛擬文件系統

虛擬文件系統1 通用文件系統2 文件系統抽象層3 Unix文件系統4 VFS對象及其數據結構其他VFS對象5 超級快對象超級塊操作6 索引節點對象索引節點操作7 目錄項對象目錄項狀態目錄項緩存目錄項操作8 文件對象9 和文件系統相關的數據結構10 和進程相關的數據結構11 Linux中的文件系統…

Oracle 查詢歷史數據(轉帖)

回復誤刪除數據信息。 1、執行 alter table table_name enable row movement; 2、執行 FlashBack table table_name to timestamp to_timestamp(2012-05-24 14:59:36,yyyy-mm-dd hh24:mi:ss); 查詢歷史操作數據信息。 比較合理的方法是先從閃回區查找出被誤刪的數據&#xff0c…

Java里面的幾種路徑的區別

1&#xff0c;相對路徑 相對路徑就是指由這個文件所在的路徑引起的跟其它文件&#xff08;或文件夾&#xff09;的路徑關系。 也就是說&#xff1a; 對于如圖所示&#xff1a;一news.html為例 在WEB15工程下的WebContent下的WEB-INF下的news.html 當我訪問的news.html的時候…

Linux內核設計與實現---塊I/O層

塊I/O層1 解刨一個塊設備2 緩沖區和緩沖區頭3 bio結構體新老方法對比4 請求隊列5 I/O調度程序I/O調度程序的工作Linus電梯最終期限I/O調度程序預測I/O調度程序完全公正的排隊I/O調度程序空操作的I/O調度程序I/O調度程序的選擇系統中能夠 隨機訪問 固定大小數據片的設備被稱為塊…

Java字符類isUpperCase()方法與示例

角色類isUpperCase()方法 (Character class isUpperCase() method) isUpperCase() method is available in java.lang package. isUpperCase()方法在java.lang包中可用。 isUpperCase() method is used to check whether the given char value is uppercase or not. isUpperCas…

javascript:history.go()和History.back()的區別

javascript:history.go()和History.back()的區別收藏 <input typebutton value刷新 οnclick"window.location.reload()"> <input typebutton value前進 οnclick"window.history.go(1)"> <input typebutton value后…

sql查詢行轉列

--SQL 面試題 /* 問題&#xff1a;假設有張學生成績表(tb)如下: 姓名 課程 分數 張三 語文 74 張三 數學 83 張三 物理 93 李四 語文 74 李四 數學 84 李四 物理 94 想變成(得到如下結果)&#xff1a; 姓名 語文 數學 物理 ---- ---- ---- ---- 李四 74 84 94 張三 74 83 93 --…

算法---數

數1 最大公約數2 最小公約數3 進制轉換4 階乘統計階乘尾部0的個數5 字符串加法減法二進制加法6 多數投票問題數組中出現次數多于n/2的元素7 相遇問題改變數組元素使所有元素都相同1 最大公約數 歐幾里得算法&#xff1a;兩個整數的最大公約數等于其中較小的那個數和兩數相除余…

Java ByteArrayOutputStream reset()方法及示例

ByteArrayOutputStream類reset()方法 (ByteArrayOutputStream Class reset() method) reset() method is available in java.io package. reset()方法在java.io包中可用。 reset() method is used to reset this stream (i.e. it removes all currently consumed output in thi…

使用存儲過程修改sql server 2005 用戶密碼

exec sp_password null,新密碼,用戶名轉載于:https://www.cnblogs.com/SXLBlog/archive/2009/07/10/1520590.html

winform TopMost

當設置winform窗體的TopMost屬性為true時&#xff0c;有時會出現不好使的狀況&#xff0c;這可能是因為窗體設置了MdiParent屬性。轉載于:https://www.cnblogs.com/magic-cube/archive/2012/06/10/2544216.html

Linux內核設計與實現---進程地址空間

進程地址空間1 內存描述符分配內存描述符銷毀內存描述符mm_struct與內核線程2 內存區域VMA標志VMA操作內存區域的樹形結構和內存區域的鏈表結構3 操作內存區域find_vma()find_vma_prev()find_vma_intersection()4 mmap()和do_mmap()&#xff1a;創建地址空間mmap&#xff08;&a…

JavaScript中帶有示例的Math.log10()方法

JavaScript | Math.log10()方法 (JavaScript | Math.log10() Method) Math operations in JavaScript are handled using functions of math library in JavaScript. In this tutorial on Math.log10() method, we will learn about the log10() method and its working with e…

JSP技術

一、jsp腳本和注釋 jsp腳本&#xff1a; 1&#xff09;<%java代碼%> ----- 內部的java代碼翻譯到service方法的內部 2&#xff09;<%java變量或表達式> ----- 會被翻譯成service方法內部out.print() 3&#xff09;<%!java代碼%> ---- 會被翻譯成servlet的成…