【C++題解】DFS和BFS

4小時編碼練習計劃,專注于深度優先搜索(DFS)和廣度優先搜索(BFS)這兩種基本且強大的算法。

下午 (4小時): 搜索算法專題——DFS與BFS

DFS和BFS是圖論和多種問題求解中的基石算法。深刻理解它們的原理、差異和代碼實現模板,是提升算法能力的必經之路。

練習計劃概覽

  • 總時長: 約 4 小時

  • 核心目標:

    1. 掌握深度優先搜索(DFS)的遞歸回溯思想,能夠寫出解決“排列組合”和“可行性搜索”問題的代碼模板。

    2. 掌握廣度優先搜索(BFS)基于隊列的層次遍歷思想,熟練使用其解決“無權圖最短路徑”問題。

    3. 清晰地辨別DFS(找所有解/任意解)和BFS(找最優解/最短步數)的典型應用場景。

    4. 學會使用visited數組或哈希表來標記狀態,避免重復搜索和死循環。


第一部分:深度優先搜索 (DFS)——“不撞南墻不回頭” (約 1.5 小時)

DFS的核心思想是“一條路走到黑”,它沿著一個分支深入探索,直到無法再前進時才回溯到上一步,換個方向繼續探索。它天然地適合用遞歸實現,常用于求解所有可能的方案。

題目編號題目名稱核心知識點練習目標
LeetCode 46 /
Luogu P1706
全排列 (Permutations)DFS, 回溯, 狀態管理DFS入門必做題。練習最基礎的DFS回溯模板。學習如何通過 visited 數組記錄哪些數字已被使用,在遞歸的每一層選擇一個未使用過的數字,并在回溯時恢復現場(pop_backvisited[i] = false)。
LeetCode 51 /
Luogu P1219
N皇后 (N-Queens)DFS, 回溯, 剪枝在“全排列”的基礎上,增加了復雜的約束條件(行、列、對角線不能沖突)。這道題能極好地鍛煉你如何在DFS的遞歸過程中進行“剪枝”——即提前判斷當前路徑是否合法,從而避免無效的深入搜索,提升效率。
題解
// 46 - 經典的使用DFS回溯
/*代碼框架bool visited[MAXN]; // 訪問標記數組void dfs(int u) {visited[u] = true; // 標記已訪問// process(u); // 處理當前節點 u 的邏輯for (int v : adj[u]) { // 遍歷 u 的所有鄰居 vif (!visited[v]) { // 如果鄰居 v 未被訪問dfs(v); // 繼續深入}}}
*/
#include <iostream>
#include <vector>using namespace std;vector<int> path;
vector<vector<int>> ans;
void bt(vector<int>& nums,vector<bool> &used){if(path.size()==nums.size()){ans.push_back(path);return;}for(int i=0;i<nums.size();i++){if(!used[i]){used[i] = true;path.push_back(nums[i]);bt(nums,used);path.pop_back();used[i] = false;}}
}
vector<vector<int>> permute(vector<int>& nums) {vector<bool> used(nums.size(),false);bt(nums,used);return ans;
}int main(){vector<int> nums ={1,2,3};      //  樣例輸入。vector<vector<int>> rst = permute(nums);for(vector<int> v : rst){for(int i : v){cout << i << " ";}cout << endl;}return 0;
}
//51 - 特別經典的問題,同樣回溯去做#include <iostream>
#include <vector>
using namespace std;class Solution {
private:vector<vector<string>> result;vector<string> board;// 檢查在(row, col)位置放置皇后是否安全bool isSafe(int row, int col, int n) {// 檢查同一列是否有皇后for (int i = 0; i < row; i++) {if (board[i][col] == 'Q') {return false;}}// 檢查左上對角線for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {if (board[i][j] == 'Q') {return false;}}// 檢查右上對角線for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {if (board[i][j] == 'Q') {return false;}}return true;}// 回溯函數void backtrack(int row, int n) {// 如果所有行都放置了皇后,找到一個解if (row == n) {result.push_back(board);return;}// 嘗試在當前行的每一列放置皇后for (int col = 0; col < n; col++) {if (isSafe(row, col, n)) {// 放置皇后board[row][col] = 'Q';// 遞歸處理下一行backtrack(row + 1, n);// 回溯,移除皇后board[row][col] = '.';}}}public:vector<vector<string>> solveNQueens(int n) {// 初始化棋盤board = vector<string>(n, string(n, '.'));result.clear();// 從第0行開始回溯backtrack(0, n);return result;}
};
int main(){int n;cin >> n;Solution solution;vector<vector<string>> rst = solution.solveNQueens(n);cout << "共找到 " << rst.size() << " 種解法:" << endl << endl;for (int i = 0; i < rst.size(); i++) {cout << "解法 " << (i + 1) << ":" << endl;for (const string& row : rst[i]) {cout << row << endl;}cout << endl;}return 0;
}

練習建議:

  • 畫遞歸搜索樹:在做這兩道題時,強烈建議您在紙上畫出當N較小(如3或4)時的遞歸搜索樹。這能非常直觀地幫助您理解“深入”、“回溯”、“剪枝”這些概念是如何在代碼中體現的。

  • 模板化:嘗試將“全排列”的代碼抽象成一個通用的回溯模板:

    void dfs(參數) {if (滿足終止條件) {// 存放結果return;}for (選擇 : 本層可選的集合) {// 處理節點dfs(路徑, 選擇列表);// 回溯,撤銷處理}
    }
    

第二部分:廣度優先搜索 (BFS)——“一層一層地毯式搜索” (約 2 小時)

BFS借助隊列實現,從起點開始,先訪問所有距離為1的鄰居,再訪問所有距離為2的鄰居,以此類推,像水波紋一樣向外擴散。這個特性決定了它能夠找到從起點到任何其他點的最短路徑(在邊權為1的圖中)。

題目編號題目名稱核心知識點練習目標
LeetCode 994腐爛的橘子
(Rotting Oranges)
BFS, 隊列, 多源BFS經典的網格BFS問題。此題是“多源BFS”的絕佳范例,即初始時有多個起點(腐爛的橘子)同時開始搜索。通過BFS的層次遍歷,可以很自然地模擬出時間一分鐘一分鐘流逝、橘子一層一層腐爛的過程。
LeetCode 127單詞接龍
(Word Ladder)
BFS, 隱式圖, 字符串處理BFS求解最短路徑的經典應用。這道題的難點在于圖是“隱式”的——節點是單詞,兩個單詞之間是否存在邊需要我們自行判斷(是否只差一個字母)。這要求我們在BFS過程中動態地尋找鄰居節點,是BFS能力的進階考驗。
題解
//994 - BFS,使用隊列作為核心觀察目前廣度搜索節點,然后一層一層去處理,每次都會記錄這次隊列的大小。#include <iostream>
#include <vector>
#include <deque>
#include <queue>
using namespace std;class Solution {
public:int orangesRotting(vector<vector<int>>& grid) {int m = grid.size();int n = grid[0].size();queue<pair<int, int>> q;int fresh = 0;// 找到所有腐爛的橘子,并統計新鮮橘子數量for(int i = 0; i < m; i++) {for(int j = 0; j < n; j++) {if(grid[i][j] == 2) {q.push({i, j});} else if(grid[i][j] == 1) {fresh++;}}}// 如果沒有新鮮橘子,直接返回0if(fresh == 0) return 0;int minutes = 0;int directions[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};while(!q.empty()) {int size = q.size();bool hasRotten = false;// 處理當前層的所有腐爛橘子for(int i = 0; i < size; i++) {pair<int, int> curr = q.front();q.pop();int x = curr.first;int y = curr.second;// 檢查四個方向for(int d = 0; d < 4; d++) {int nx = x + directions[d][0];int ny = y + directions[d][1];// 邊界檢查和新鮮橘子檢查if(nx >= 0 && nx < m && ny >= 0 && ny < n && grid[nx][ny] == 1) {grid[nx][ny] = 2;  // 變腐爛q.push({nx, ny});fresh--;hasRotten = true;}}}// 如果這一輪有橘子腐爛,時間+1if(hasRotten) {minutes++;}}// 如果還有新鮮橘子,返回-1;否則返回時間return fresh == 0 ? minutes : -1;}
};int main(){Solution solution;// 測試用例1: [[2,1,1],[1,1,0],[0,1,1]]// 預期輸出: 4vector<vector<int>> grid1 = {{2,1,1},{1,1,0},{0,1,1}};cout << "測試用例1: " << solution.orangesRotting(grid1) << endl;// 測試用例2: [[2,1,1],[0,1,1],[1,0,1]]// 預期輸出: -1vector<vector<int>> grid2 = {{2,1,1},{0,1,1},{1,0,1}};cout << "測試用例2: " << solution.orangesRotting(grid2) << endl;// 測試用例3: [[0,2]]// 預期輸出: 0vector<vector<int>> grid3 = {{0,2}};cout << "測試用例3: " << solution.orangesRotting(grid3) << endl;// 測試用例4: [[1]]// 預期輸出: -1vector<vector<int>> grid4 = {{1}};cout << "測試用例4: " << solution.orangesRotting(grid4) << endl;return 0;
}
//127 - 轉化成圖求最短路徑即可,我這里一開始想的dijstra,交上去雖然過了,但是耗時比較多,因為這計算了每個節點的距離,所有還有一種BFS也在里面,時間要節省一些。#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <climits>
#include <queue>
using namespace std;class Solution {
private:int n; // 目標節點索引
public:bool similar(string a, string b) {int diff = 0;for (int i = 0; i < a.size(); i++) {if (a[i] != b[i]) {diff++;}}return diff == 1;}int dijstra(vector<vector<bool>>& grid, int target) {int size = grid.size();vector<int> dij(size, size + 1);vector<bool> check(size, false);dij[0] = 0;for (int count = 0; count < size; count++) {int minDist = INT_MAX;int u = -1;// 找到未訪問節點中距離最小的節點for (int i = 0; i < size; i++) {if (!check[i] && dij[i] < minDist) {minDist = dij[i];u = i;}}if (u == -1) break; // 沒有可達節點check[u] = true;// 更新相鄰節點的距離for (int v = 0; v < size; v++) {if (!check[v] && grid[u][v] && dij[u] != INT_MAX) {if (dij[u] + 1 < dij[v]) {dij[v] = dij[u] + 1;}}}}return dij[target] == size + 1 ? -1 : dij[target];}int bfs(vector<vector<bool>>& grid, int target) {int size = grid.size();vector<int> dist(size, -1);queue<int> q;q.push(0);dist[0] = 0;while (!q.empty()) {int curr = q.front();q.pop();if (curr == target) {return dist[target];}// 遍歷所有相鄰節點for (int next = 0; next < size; next++) {if (grid[curr][next] && dist[next] == -1) {dist[next] = dist[curr] + 1;q.push(next);}}}return dist[target];}int ladderLength(string beginWord, string endWord,vector<string>& wordList, bool useBFS = true) {vector<vector<bool>> grid(wordList.size() + 2,vector<bool>(wordList.size() + 2, false));if (find(wordList.begin(), wordList.end(), endWord) == wordList.end()) {return 0;}for (int i = 0; i < wordList.size(); i++) {if (similar(beginWord, wordList[i])) {grid[0][i + 1] = true;}}for (int i = 0; i < wordList.size(); i++) {if (endWord == wordList[i]) {n = i + 1;}for (int j = i; j < wordList.size(); j++) {if (similar(wordList[j], wordList[i])) {grid[i + 1][j + 1] = true;grid[j + 1][i + 1] = true;}}}int result;if (useBFS) {cout << "使用BFS算法..." << endl;result = bfs(grid, n);} else {cout << "使用Dijkstra算法..." << endl;result = dijstra(grid, n);}return result == -1 ? 0 : result + 1;}
};int main() {Solution solution;cout << "請選擇算法:" << endl;cout << "1. BFS (廣度優先搜索)" << endl;cout << "2. Dijkstra (迪杰斯特拉算法)" << endl;cout << "請輸入選擇 (1 或 2): ";int choice;cin >> choice;bool useBFS = (choice == 1);cout << "\n開始測試..." << endl;// 測試用例1string beginWord1 = "hit";string endWord1 = "cog";vector<string> wordList1 = {"hot","dot","dog","lot","log","cog"};cout << "\n測試用例1: beginWord=\"hit\", endWord=\"cog\"" << endl;cout << "wordList=[\"hot\",\"dot\",\"dog\",\"lot\",\"log\",\"cog\"]" << endl;int result1 = solution.ladderLength(beginWord1, endWord1, wordList1, useBFS);cout << "結果: " << result1 << endl;// 測試用例2string beginWord2 = "hit";string endWord2 = "cog";vector<string> wordList2 = {"hot","dot","dog","lot","log"};cout << "\n測試用例2: beginWord=\"hit\", endWord=\"cog\"" << endl;cout << "wordList=[\"hot\",\"dot\",\"dog\",\"lot\",\"log\"]" << endl;int result2 = solution.ladderLength(beginWord2, endWord2, wordList2, useBFS);cout << "結果: " << result2 << endl;return 0;
}

練習建議:

  • 隊列是核心:牢記BFS的核心數據結構是隊列。queue.push() 對應將新節點加入下一層待訪問列表,queue.pop() 對應取出當前層的節點進行處理。

  • 距離/步數 tracking: 通常需要一個dist數組或 map 來記錄從起點到每個節點的最短距離。在BFS中,dist[neighbor] = dist[current] + 1 是更新距離的關鍵。對于“腐爛的橘子”,BFS的層數本身就代表了分鐘數。

  • 避免重復訪問:和DFS一樣,visited 數組或 set 對于BFS至關重要,用于確保每個節點只入隊一次,防止在有環的圖中無限循環。


目標達成自查 (約 0.5 小時)

完成以上練習后,進行復盤和總結:

  1. DFS vs. BFS 的核心區別是什么?

    • 數據結構:DFS主要依賴遞歸(系統棧),BFS依賴隊列。

    • 搜索順序:DFS是“深度”優先,一路走到底;BFS是“廣度”優先,一層層擴展。

  2. 如何根據問題選擇算法?

    • 問題要求最短路徑/最少步數/最少操作次數嗎? -> 首選 BFS

    • 問題要求找出所有方案/組合/排列,或者只是判斷“能不能”到達某個狀態(不要求最短)? -> DFS 更自然

  3. 兩種搜索算法的通用模板是什么?

    • 我能否獨立、無誤地寫出DFS回溯和BFS層次遍歷的基本代碼框架?

    • 我是否清楚在模板的哪個位置進行“訪問”標記(visited)可以避免重復計算?(對于BFS,在節點入隊時標記;對于DFS,在剛進入遞歸函數時標記)。

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

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

相關文章

Android模擬簡單的網絡請求框架Retrofit實現

文章目錄1.靜態代理2.動態代理3.實現簡單的Retrofit定義對應的請求注解參數通過動態代理模擬Retrofit的創建請求參數的處理定義請求接口測試請求1.靜態代理 代理默認給某一個對象提供一個代理對象&#xff0c;并由代理對象控制對原對象的引用。通俗來講&#xff0c;代理模式就…

Matter安全實現

Matter分析與安全驗證 上一篇文章簡單的介紹了Matter的架構、實現、以及部分安全驗證過程&#xff1b;這里繼續補充一下Matter的其他安全驗證流程&#xff0c;以更好的實現Matter安全。 Matter提供的安全實現流程大概總結起來是這個流程 硬件信任根→安全啟動→動態證書→加密…

從基礎到實踐:Web核心概念與Nginx入門全解析

從基礎到實踐&#xff1a;Web核心概念與Nginx入門全解析 文章目錄從基礎到實踐&#xff1a;Web核心概念與Nginx入門全解析一、Web是什么&#xff1f;從基本概念到核心架構1.1 Web的本質&#xff1a;一個超文本信息系統1.2 B/S架構&#xff1a;Web的“前端-后端”分工模式二、一…

【完整源碼+數據集+部署教程】加工操作安全手套與手部檢測系統源碼和數據集:改進yolo11-cls

背景意義 研究背景與意義 隨著工業自動化和智能制造的迅速發展&#xff0c;工人安全問題日益受到重視。特別是在涉及重型機械和危險操作的工作環境中&#xff0c;工人手部的安全保護顯得尤為重要。傳統的安全手套雖然在一定程度上能夠保護工人的手部&#xff0c;但在復雜的加工…

代碼隨想錄算法訓練營第一天 || (雙指針)27.移除元素 26.刪除有序數組中的重復項 283.移動零 977.有序數組的平方

代碼隨想錄算法訓練營第一天 || (雙指針)27.移除元素 26.刪除有序數組中的重復項 283.移動零 27.移除元素 暴力方法 同向雙指針雙指針 自己AC的解答 卡哥的講解 26.刪除有序數組中的重復項 同向雙指針 283.移動零 自己解答 靈神做法(同向雙指針+交換) 977.有序數組的平方 暴…

Java全棧開發工程師面試實錄:從基礎到實戰的深度探討

Java全棧開發工程師面試實錄&#xff1a;從基礎到實戰的深度探討 一、初識與自我介紹 面試官&#xff08;李工&#xff09;&#xff1a; 你好&#xff0c;歡迎來到我們公司。我是負責技術面試的李工&#xff0c;今天我們將進行一場關于Java全棧開發的深入交流。你可以先簡單介紹…

Kafka:Java開發的消息神器,你真的懂了嗎?

Kafka&#xff1a;Java開發的消息神器&#xff0c;你真的懂了嗎&#xff1f; 一、Kafka 是什么鬼&#xff1f; 想象一下&#xff0c;你在網上瘋狂剁手后&#xff0c;滿心期待著快遞包裹的到來。這時候&#xff0c;快遞站就像是 Kafka&#xff0c;而你的包裹就是消息。快遞站接…

深度學習之第八課遷移學習(殘差網絡ResNet)

目錄 簡介 一、遷移學習 1.什么是遷移學習 2. 遷移學習的步驟 二、殘差網絡ResNet 1.了解ResNet 2.ResNet網絡---殘差結構 三、代碼分析 1. 導入必要的庫 2. 模型準備&#xff08;遷移學習&#xff09; 3. 數據預處理 4. 自定義數據集類 5. 數據加載器 6. 設備配置…

Pinia 兩種寫法全解析:Options Store vs Setup Store(含實踐與場景對比)

目標&#xff1a;把 Pinia 的兩種寫法講透&#xff0c;寫明“怎么寫、怎么用、怎么選、各自優缺點與典型場景”。全文配完整代碼與注意事項&#xff0c;可直接當團隊規范參考。一、背景與準備 適用版本&#xff1a;Vue 3 Pinia 2.x安裝與初始化&#xff1a; # 安裝 npm i pini…

setup函數相關【3】

目錄1.setup函數&#xff1a;1.概述&#xff1a;2.案例分析&#xff1a;2.setup函數的優化&#xff1a;&#xff08;setup語法糖&#xff09;優化1&#xff1a;優化2&#xff1a;安裝插件&#xff1a;安裝指令&#xff1a;只對當前項目安裝配置vite.config.ts&#xff1a;代碼編…

如何通過AI進行數據資產梳理

最終產出 數據資產清單 包含所有數據資產的詳細目錄,列出數據集名稱、描述、所有者、格式、存儲位置和元數據。 用途:幫助政府部門清晰了解數據資產分布和狀態。 數據質量報告 數據質量評估結果,記錄準確性、完整性、一致性等問題及改進建議,基于政府認可的數據質量框架(如…

【傳奇開心果系列】Flet框架結合pillow實現的英文文字倒映特效自定義模板特色和實現原理深度解析

Flet框架結合pillow實現的英文文字倒映特效自定義模板特色和實現原理深度解析 一、效果展示截圖 二、使用場景 三、特色說明 四、概括說明 五、依賴文件列表 六、安裝依賴命令 七、 項目結構建議 八、注意事項 九、Flet 文字倒影效果實現原理分析 (一)組件結構與功能 1. 圖像…

2025最新深度學習面試必問100題--理論+框架+原理+實踐 (下篇)

2025最新深度學習面試必問100題–理論框架原理實踐 (下篇) 在上篇中&#xff0c;我們已經深入探討了機器學習基礎、CNN、RNN及其變體&#xff0c;以及模型優化的核心技巧。 在下篇中&#xff0c;我們將把目光投向更遠方&#xff0c;聚焦于當今AI領域最炙手可熱的前沿。我們將深…

原子工程用AC6編譯不過問題

…\Output\atk_h750.axf: Error: L6636E: Pre-processor step failed for ‘…\User\SCRIPT\qspi_code.scf.scf’修改前&#xff1a; #! armcc -E ;#! armclang -E --targetarm-arm-none-eabi -mcpucortex-m7 -xc /* 使用說明 ! armclang -E --targetarm-arm-none-eabi -mcpuco…

Python有哪些經典的常用庫?(第一期)

目錄 1、NumPy (數值計算基礎庫) 核心特點&#xff1a; 應用場景&#xff1a; 代碼示例&#xff1a; 2、Pandas (數據分析處理庫) 應用場景&#xff1a; 代碼示例&#xff1a; 3、Scikit-learn (機器學習庫) 核心特點&#xff1a; 應用場景&#xff1a; 代碼示例&am…

現代 C++ 高性能程序驅動器架構

&#x1f9e0; 現代 C 高性能程序驅動器架構M/PA&#xff08;多進程&#xff09;是隔離的“孤島”&#xff0c;M/TA&#xff08;多線程&#xff09;是共享的“戰場”&#xff0c;EDSM&#xff08;事件驅動&#xff09;是高效的“反應堆”&#xff0c;MDSM&#xff08;消息驅動&…

投資儲能項目能賺多少錢?小程序幫你測算

為解決電網負荷平衡、提升新能源消納等問題&#xff0c;儲能項目的投資開發越來越多。那么&#xff0c;投資儲能項目到底能賺多少錢&#xff1f;適不適合投資&#xff1f;用“綠蟲零碳助手”3秒鐘精準測算。操作只需四步&#xff0c;簡單易懂&#xff1a;1.快速登錄&#xff1a…

Mac 能夠連Wife,但是不能上網問題解決

請按照以下步驟從最簡單、最可能的原因開始嘗試&#xff1a; 第一步&#xff1a;基礎快速排查 這些步驟能解決大部分臨時性的小故障。 重啟設備&#xff1a;關閉您的 Mac 和路由器&#xff0c;等待一分鐘后再重新打開。這是解決網絡問題最有效的“萬能藥”。檢查其他設備&am…

基于SpringBoot的旅游管理系統的設計與實現(代碼+數據庫+LW)

摘要 本文闡述了一款基于SpringBoot框架的旅游管理系統設計與實現。該系統整合了用戶信息管理、旅游資源展示、訂單處理流程及安全保障機制等核心功能&#xff0c;專為提升旅游行業的服務質量和運營效率而設計。 系統采用前后端分離架構&#xff0c;前端界面設計注重跨設備兼…

Springboot樂家流浪貓管理系統16lxw(程序+源碼+數據庫+調試部署+開發環境)帶論文文檔1萬字以上,文末可獲取,系統界面在最后面。

系統程序文件列表項目功能&#xff1a;領養人,流浪貓,領養申請開題報告內容基于Spring Boot的樂家流浪貓管理系統開題報告一、研究背景與意義隨著城市化進程加速和人口增長&#xff0c;流浪貓問題已成為全球性社會挑戰。據統計&#xff0c;全球每年約有1.5億只無家可歸的寵物&a…