演算法系列 · 第 15 關 · 搜尋

深度優先搜尋 DFS

一條路走到底,撞牆再回頭。用堆疊或遞迴,是回溯與拓樸排序的基礎。

DFS(Depth-First Search)跟 BFS 相反:它一條路走到底,走不動(撞牆或死路)才回頭(backtrack)換另一條。引擎是堆疊(或直接用遞迴,底層就是呼叫堆疊)。它不保證最短路,但特別適合「走遍所有可能、偵測環、拓樸排序、回溯解題」。

核心觀念:一路到底再回頭

堆疊 / 遞迴當引擎 —— 一直往深處走,把「還沒試的分岔」記在堆疊上。

走到死路就回溯 —— 沒有新鄰居可走,就退回上一個分岔,換一條沒走過的。

visited 一樣要記 —— 避免重複、避免在有環的圖裡繞不完。

不保證最短 —— DFS 找到的是「一條」路,不是最短那條(要最短用 BFS)。

遞迴 vs 迭代(兩種寫法)

遞迴版 —— 最簡潔:函式自己呼叫自己,「回溯」就是 return 回上一層。底層用的正是程式的呼叫堆疊。

迭代版 —— 自己開一個 list 當堆疊,pop 最後放進去的。資料很深時避免遞迴爆掉(RecursionError)。

兩種等價 —— 把 BFS 的佇列換成堆疊,就從 BFS 變 DFS(範例 10)。

複雜度

O(V+E)時間 —— 每個節點走一次、每條邊看一次(跟 BFS 一樣)。
O(R·C)時間(迷宮) —— R×C 格,每格看一次。
O(V)空間 —— 堆疊 / 遞迴深度最壞是所有節點(一條長鏈)。
DFS vs BFS 一句話:BFS 找「最短步數」(佇列、一層層);DFS 找「走得通嗎 / 所有走法 / 有沒有環」(堆疊、一路到底)。回溯法(排列、數獨、N 皇后)本質都是 DFS。

用生活比喻它

🧭

走迷宮靠右手

沿一條路一直走,撞牆就退回上一個路口,換一條沒走過的繼續 —— 這就是回溯。

🌳

爬樹枝

沿一根樹枝爬到底端的葉子,再退回岔口爬另一根,直到爬遍。

📂

資料夾往下鑽

打開一個資料夾就一直往裡點,點到最底再退回上一層看別的。

🎮 迷宮裡的 DFS(一路鑽 + 撞牆回頭)

同款迷宮,這次看 DFS 怎麼走。它依「右→下→左→上」的順序一路鑽到底:琥珀是目前這條路(堆疊)、淡綠是走過但發現是死路、退回來的格子。注意它一開始往右鑽進死角,撞牆後回溯再換方向 —— 這正是 BFS 的「水波」沒有的行為。

10 個程式碼範例(Python)

從 DFS 遞迴 / 迭代、迷宮找路、回溯框架,到島嶼數量、偵測環、拓樸排序、全排列。

顯示範例:

常見陷阱

❌ 拿 DFS 找最短路 —— DFS 不保證最短。要最短用 BFS(不加權)或 Dijkstra(加權)。

❌ 遞迴太深爆掉 —— Python 預設遞迴上限約 1000,資料很深會 RecursionError,改用迭代堆疊或調高上限。

❌ 回溯忘了收回 —— 選一步遞迴後若失敗,一定要 pop 收回,不然狀態會髒掉、答案全錯。

❌ 忘了 visited —— 有環的圖會無限鑽。

🎯 換你了(先別急著查答案)

把答案打到對話裡,我幫你對。