BFS 和 DFS 編程思想、框架、技巧及經典例題總結

BFS 和 DFS 編程思想、框架、技巧及經典例題總結

一、核心編程思想

  1. BFS(廣度優先搜索)
  • 核心思想:以「層次遍歷」為核心,從起點出發,優先探索距離起點最近的節點,逐層擴散遍歷。
  • 本質:通過「隊列」實現 “先入先出” 的順序,保證每次處理的都是當前層的所有節點,適合解決「最短路徑」「層次遍歷」「連通性判斷(無權圖)」等問題。
  • 特點:
    • 遍歷過程中節點的 “距離”(步數)是遞增的,首次到達目標節點時的路徑即為最短路徑(無權圖 / 等權圖)。
    • 空間復雜度較高(需存儲當前層所有節點),但時間復雜度穩定。
  1. DFS(深度優先搜索)
  • 核心思想:以「深度優先」為核心,從起點出發,沿著一條路徑深入到底,直到無法前進時回溯,換另一條路徑繼續探索。
  • 本質:通過「遞歸棧」或「手動棧」實現 “后入先出” 的順序,適合解決「連通性判斷」「排列組合」「路徑搜索」「回溯剪枝」等問題。
  • 特點:
    • 遍歷過程具有 “回溯” 特性,可通過剪枝優化無效路徑,減少計算量。
    • 空間復雜度由遞歸深度(或棧深度)決定,可能因深度過大導致棧溢出(需注意遞歸邊界)。

二、通用編程框架

  1. BFS 框架(模板)
    基礎模板(樹 / 圖通用)
    def bfs(start, target):# 初始化隊列(存儲待處理節點)from collections import dequequeue = deque([start])# 初始化visited(避免重復訪問,圖必備;樹可省略,但需處理父節點回退)visited = set([start])# 記錄距離/層數(可選,按需添加)distance = 0  while queue:# 處理當前層的所有節點(關鍵:先獲取當前層節點數)level_size = len(queue)for _ in range(level_size):# 彈出隊首節點current = queue.popleft()# 若找到目標,返回結果if current == target:return distance  # 或其他結果# 遍歷當前節點的所有相鄰節點for neighbor in get_neighbors(current):if neighbor not in visited:visited.add(neighbor)queue.append(neighbor)# 層數/距離+1(每處理完一層遞增)distance += 1# 未找到目標return -1
    
    • 關鍵組件:
      • 隊列:存儲待處理的節點,保證 “先入先出”。
      • visited 集合:標記已訪問節點(圖中必用,避免環導致的死循環;樹中可省略,但需通過父節點指針避免回退)。
      • 層次處理:通過level_size控制每層節點的批量處理,實現 “層次遍歷”。
  2. DFS 框架(模板)
    遞歸版(簡潔,適合深度不太大的場景)
    def dfs(current, visited, target):# 終止條件:找到目標或越界if current == target:return True  # 或記錄路徑if current is invalid: # 剪枝return False# 標記當前節點為已訪問visited.add(current)# 遍歷所有相鄰節點for neighbor in get_neighbors(current):if neighbor not in visited:# 遞歸探索下一層if dfs(neighbor, visited, target):return True  # 找到目標,提前返回# 回溯:若需恢復狀態(如排列組合問題),此處可移除標記# visited.remove(current)  # 視問題而定是否需要return False  # 未找到目標
    
    迭代版(手動棧,適合深度較大的場景,避免遞歸棧溢出)
    def dfs_iterative(start, target):stack = [start]  # 手動棧visited = set([start])while stack:current = stack.pop()  # 彈出棧頂節點(最后入棧的節點)# 找到目標,返回結果if current == target:return True# 遍歷相鄰節點(注意:棧是后入先出,若需按順序遍歷,可反向添加)for neighbor in reversed(get_neighbors(current)):if neighbor not in visited:visited.add(neighbor)stack.append(neighbor)return False
    
    • 關鍵組件:
      • 棧:遞歸時隱式使用函數調用棧,迭代時手動維護棧,保證 “后入先出”。
      • visited 集合:同 BFS,避免重復訪問。
      • 回溯:在排列組合等問題中,需在遞歸返回后移除visited標記,允許節點被其他路徑重新訪問。

三、實用技巧

  1. BFS 技巧
  • 雙向 BFS 優化:當起點和終點明確時(如最短路徑問題),可從起點和終點同時開始 BFS,相遇時終止,大幅減少搜索空間(適用于節點數量龐大的場景,如「打開轉盤鎖」)。
  • 距離記錄:通過數組或哈希表記錄每個節點到起點的距離,無需額外變量(如distance[node] = distance[current] + 1)。
  • 層次信息利用:在層次遍歷問題中(如二叉樹每層節點之和),通過level_size區分不同層,方便按層處理數據。
  1. DFS 技巧
  • 剪枝優化:在遞歸過程中提前判斷無效路徑(如 “和超過目標的組合”“已存在重復的排列”),直接返回,減少計算量。
  • 記憶化搜索:對重復子問題(如動態規劃中的遞歸場景),用哈希表記錄已計算的結果,避免重復計算(如「斐波那契數列」「最長遞增子序列」的遞歸實現)。
  • 路徑跟蹤:在需要記錄具體路徑的問題中(如「所有可能的路徑」),通過列表在遞歸時添加節點,回溯時移除節點,動態維護路徑。

四、經典例題解析

  1. BFS 經典例題
    例題 1:二叉樹的層序遍歷(LeetCode 102)
    • 問題:給定二叉樹的根節點,返回按層序遍歷的節點值(逐層從左到右)。
    • 思路:用 BFS 按層處理節點,每層遍歷前記錄隊列長度(當前層節點數),依次彈出并收集值,再將子節點入隊。
    • 代碼:
    def levelOrder(root):if not root:return []from collections import dequequeue = deque([root])result = []while queue:level_size = len(queue)current_level = []for _ in range(level_size):node = queue.popleft()current_level.append(node.val)if node.left:queue.append(node.left)if node.right:queue.append(node.right)result.append(current_level)return result
    
    例題 2:打開轉盤鎖(LeetCode 752)
    • 問題:轉盤鎖有 4 個數字(0-9),每次可轉動一個數字 ±1(如 0→9 或 0→1)。給定死亡數字列表(不可經過),求從 “0000” 到目標數字的最少步數,無法到達則返回 - 1。
    • 思路:BFS 求最短路徑,每次轉動生成 8 種可能的下一個狀態(4 個位置,每個 ±1),用 visited 和死亡列表過濾無效狀態,- 雙向 BFS 可優化效率。
    • 核心代碼:
    def openLock(deadends, target):from collections import dequedead = set(deadends)start = "0000"if start in dead:return -1if start == target:return 0# BFS初始化queue = deque([start])visited = set([start])steps = 0while queue:steps += 1level_size = len(queue)for _ in range(level_size):current = queue.popleft()# 生成所有可能的下一個狀態for i in range(4):for d in (-1, 1):# 轉動第i位數字(0-9循環)new_digit = (int(current[i]) + d) % 10next_str = current[:i] + str(new_digit) + current[i+1:]if next_str == target:return stepsif next_str not in visited and next_str not in dead:visited.add(next_str)queue.append(next_str)return -1
    
  2. DFS 經典例題
    例題 1:島嶼數量(LeetCode 200)
    • 問題:給定二維網格,‘1’ 表示陸地,‘0’ 表示水,求島嶼數量(相鄰陸地相連,上下左右為相鄰)。
    • 思路:遍歷網格,遇到未訪問的 ‘1’ 時啟動 DFS,將所有相連的 ‘1’ 標記為已訪問(避免重復計數),每啟動一次 DFS 即增加一個島嶼。也可以使用BFS。
    • 代碼:
    def numIslands(grid):if not grid:return 0rows, cols = len(grid), len(grid[0])visited = [[False for _ in range(cols)] for _ in range(rows)]count = 0def dfs(i, j):# 越界或非陸地或已訪問,直接返回if i < 0 or i >= rows or j < 0 or j >= cols or grid[i][j] == '0' or visited[i][j]:returnvisited[i][j] = True  # 標記為已訪問# 遞歸探索上下左右dfs(i+1, j)dfs(i-1, j)dfs(i, j+1)dfs(i, j-1)for i in range(rows):for j in range(cols):if grid[i][j] == '1' and not visited[i][j]:count += 1dfs(i, j)  # 標記整個島嶼return count
    
    例題 2:全排列(LeetCode 46)
    • 問題:給定不含重復數字的數組,返回所有可能的全排列。
    • 思路:DFS 回溯法,通過遞歸探索每個位置的可能數字,用 visited 標記已使用的數字,回溯時恢復狀態以嘗試其他組合。
    • 代碼:
    def permute(nums):result = []n = len(nums)def backtrack(path, visited):# 終止條件:路徑長度等于數組長度if len(path) == n:result.append(path.copy())returnfor i in range(n):if not visited[i]:# 選擇當前數字visited[i] = Truepath.append(nums[i])# 遞歸下一層backtrack(path, visited)# 回溯:撤銷選擇path.pop()visited[i] = Falsebacktrack([], [False]*n)return result
    

五、BFS 與 DFS 的區別與適用場景

維度BFSDFS
數據結構隊列(先入先出)棧 / 遞歸棧(后入先出)
適用場景最短路徑(無權圖)、層次遍歷、拓撲排序排列組合、連通性、回溯剪枝、路徑搜索
空間復雜度較高(取決于最寬層節點數)較高(取決于遞歸深度)
特點首次到達目標即最短路徑可通過回溯剪枝優化效率

通過掌握上述框架和技巧,可快速解決大部分 BFS/DFS 相關問題。核心是理解 “層次擴散” 與 “深度探索 + 回溯” 的本質,并根據問題場景選擇合適的算法。

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

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

相關文章

【面試場景題】日志去重與統計系統設計

文章目錄題目場景描述要求問題考察點解答思考一、核心解決方案&#xff08;基礎版&#xff0c;單節點32GB內存、10臺節點&#xff09;1. 整體架構選型2. 關鍵步驟詳解&#xff08;1&#xff09;數據分片&#xff1a;解決“數據量大&#xff0c;單節點處理不了”的問題&#xff…

【Day 16】Linux-性能查看

目錄 一、Stress系統壓力測試工具 二、性能查看 &#xff08;一&#xff09;查看CPU # nproc # lscpu # top # uptime # mpstat 數字1 數字2 &#xff08;二&#xff09;查看內存 # dmidecode -t memory | less # free -h # …

【ICCV2017】Deformable Convolutional Networks

一、摘要盡管卷積神經網絡&#xff08;CNN&#xff09;在視覺識別任務上取得巨大成功&#xff0c;但其固有的固定幾何結構&#xff08;固定卷積采樣網格、固定池化窗口、固定 RoI 劃分&#xff09;嚴重限制了對未知幾何變換&#xff08;尺度、姿態、形變、視角變化&#xff09;…

echarts在前后端分離項目中的實踐與應用

目錄 一、ECharts簡介 二、后端數據接口設計 三、數據結構設計 1. 柱狀圖數據結構 2. 餅圖數據結構 四、后端實現要點 五、前端ECharts配置解析 1. 柱狀圖配置 2. 餅圖配置 六、最佳實踐建議 七、總結 一、ECharts簡介 ECharts是百度開源的一個基于JavaScript的可視…

SQL 四大語言分類詳解:DDL、DML、DCL、DQL

SQL&#xff08;結構化查詢語言&#xff09;通常被分為四種主要類型&#xff0c;每種類型負責不同的數據庫操作。下面我將詳細介紹這四類SQL語言的語法和用途。一、DDL (Data Definition Language) 數據定義語言功能&#xff1a;定義和管理數據庫對象結構&#xff08;表、視圖、…

ESP-idf框架下的HTTP服務器\HTML 485溫濕度采集并長傳

項目描述:本項目采用485采集溫濕度以及電壓電流等,485模塊分別為下圖,串口轉485模塊采用自動收發模塊,ESP32工作在AP熱點模式,通過手機連接esp32的熱點來和esp進行數據通訊,使用esp32作為HTTP服務器缺陷:項目的最終HTML頁面代碼可發給AI讓其寫注釋#include "freertos/Free…

雅江工程解鎖墨脫秘境:基礎條件全展示(區位、地震、景點、天氣)

目錄 前言 一、區位信息 1、空間位置 2、區位介紹 二、地震信息 1、歷史地震信息 2、5.0級以上大地震 三、景點信息 1、景點列表分布 2、4A級以上景點 四、天氣信息 1、天氣實況 2、天氣應對挑戰 五、總結 前言 相信最近大家對雅江電站的超級大工程項目應該有所耳…

??機器學習貝葉斯算法

??一、引言??在當今機器學習領域&#xff0c;貝葉斯算法猶如一顆璀璨的明星。你是否想過&#xff0c;垃圾郵件過濾系統是如何準確判斷一封郵件是否為垃圾郵件的呢&#xff1f;這背后可能就有貝葉斯算法的功勞。今天&#xff0c;我們就一同走進貝葉斯算法的世界&#xff0c;…

Chisel芯片開發入門系列 -- 18. CPU芯片開發和解釋8(流水線架構的代碼級理解)

以【5 Stage pipeline CPU】搜索圖片&#xff0c;選取5幅有代表性的圖列舉如下&#xff0c;并結合Chisel代碼進行理解和點評。 圖1&#xff1a;原文鏈接如下 https://acsweb.ucsd.edu/~dol031/posts/update/2023/04/10/5stage-cpu-pipeline.html 點評&#xff1a;黑色的部分…

Docker容器中文PDF生成解決方案

在Docker容器中生成包含中文內容的PDF文件時&#xff0c;經常遇到中文字符顯示為方塊或亂碼的問題。本文將詳細介紹如何在Docker環境中配置中文字體支持&#xff0c;實現完美的中文PDF生成。 問題現象 當使用wkhtmltopdf、Puppeteer或其他PDF生成工具時&#xff1a; 中文字符…

2.java集合,線程面試題(已實踐,目前已找到工作)

1線程的創建方式 繼承Thread類實現Runnable接口實現Callable接口 2.這三種方式在項目中的使用有哪些&#xff0c;一般都是怎么用的 繼承thread類實現線程的方式通過實現run方法來實現線程&#xff0c;通過run進行線程的啟用實現runnable方法實現run方法&#xff0c;然后通過thr…

站在前端的角度,看鴻蒙頁面布局

從Web前端轉向鴻蒙&#xff08;HarmonyOS&#xff09;開發時&#xff0c;理解其頁面布局的相似與差異是快速上手的核心。鴻蒙的ArkUI框架在布局理念上與Web前端有諸多相通之處&#xff0c;但也存在關鍵區別。以下從五個維度系統分析&#xff1a; &#x1f4e6; 一、盒子模型&a…

JavaWeb遺傳算法、TSP、模擬退火、ACO算法等實戰應用

Java Web中實現遺傳算法的應用 以下是關于Java Web中實現遺傳算法的應用場景和實例的整理,涵蓋不同領域的解決方案和實現方法: 遺傳算法基礎結構 在Java Web中實現遺傳算法通常需要以下核心組件: 種群初始化:隨機生成初始解集。 適應度函數:評估個體優劣。 選擇操作:輪…

【圖像算法 - 09】基于深度學習的煙霧檢測:從算法原理到工程實現,完整實戰指南

一、項目背景與需求 視頻介紹 【圖像算法 - 09】基于深度學習的煙霧檢測&#xff1a;從算法原理到工程實現&#xff0c;完整實戰指南今天我們使用深度學習來訓練一個煙霧明火檢測系統。這次我們使用了大概一萬五千張圖片的數據集訓練了這次的基于深度學習的煙霧明火檢測模型&a…

間接制冷技術概念及特征

1、基本概念 (1)間接制冷技術即二次制冷技術。常規做法:二次冷卻液儲液罐增加放置于制冷系統管路,促使冷量再快捷的傳遞給載冷劑,繼而載冷劑冷量促使冷庫達到制冷效果。間接制冷技術:通過常壓的二次冷卻介質進行大循環傳送冷量,在直接制冷劑不易應用的位置或者不可運用直…

Antlr學習筆記 01、maven配置Antlr4插件案例Demo

文章目錄前言源碼插件描述pom引入插件案例&#xff1a;實現hello 標識符 案例1、引入Antlr4的pom運行依賴2、定義語義語法&#xff0c;配置.g4文件實現java代碼3、編寫完之后&#xff0c;執行命令實現編譯4、編寫單測測試使用參考文章資料獲取前言 博主介紹&#xff1a;?目前…

PostGIS面試題及詳細答案120道之 (101-110 )

《前后端面試題》專欄集合了前后端各個知識模塊的面試題&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

第十七天:原碼、反碼、補碼與位運算

原碼、反碼、補碼與位運算 一、原碼、反碼、補碼 1、原碼 定義&#xff1a;原碼是一種簡單的機器數表示法。對于一個有符號整數&#xff0c;最高位為符號位&#xff0c; 0 表示正數&#xff0c; 1 表示負數&#xff0c;其余位表示數值的絕對值。示例&#xff1a;以 8 位二進制…

一次完整的 Docker 啟動失敗排錯之旅:從 `start-limit` 到 `network not found

一次完整的 Docker 啟動失敗排錯之旅&#xff1a;從 start-limit 到 network not found 你是否也曾自信地敲下 sudo systemctl start docker&#xff0c;卻只得到一個冰冷的 failed&#xff1f;這是一個開發者和運維工程師都可能遇到的場景。本文將通過一個真實的排錯案例&…

Tdengine 時序庫年月日小時分組匯總問題

年月分組select to_char(collection_time ,"yyyy-mm") AS date, cast(SUM(a.stage_value)as DOUBLE) as stage_value from TABLE GROUP BY date年月日分組select to_char(collection_time ,"yyyy-mm-dd") AS date, SUM(a.stage_value)as DOUBLE) as stage_…