【數據結構】圖論核心算法解析:深度優先搜索(DFS)的縱深遍歷與生成樹實戰指南?

深度優先搜索

  • 導讀:從廣度到深度,探索圖的遍歷奧秘
  • 一、深度優先搜索
  • 二、算法思路
  • 三、算法邏輯
  • 四、算法評價
  • 五、深度優先生成樹
  • 六、有向圖與無向圖
  • 結語:深潛與回溯,揭開圖論世界的另一面

深度優先遍歷

導讀:從廣度到深度,探索圖的遍歷奧秘

大家好,很高興又和大家見面啦!!!

在上一篇中,我們共同揭開了廣度優先搜索(BFS)的神秘面紗:它以“分層擴散”的方式遍歷圖結構,借助隊列實現層序遍歷,擅長解決最短路徑和連通性分析問題(例如社交網絡中的好友推薦)。BFS如同一束光波,由近及遠均勻覆蓋每個角落,確保無遺漏地探索所有可能性。

而今天,我們將潛入另一種經典策略——深度優先搜索(DFS)。與BFS的“廣撒網”不同,DFS更像一位執著探險家,認準一條路走到盡頭,再回溯尋找新路徑。這種策略在迷宮探索、拓撲排序、環路檢測等場景中大放異彩。

為何需要DFS?關鍵差異一目了然👇

  • 遍歷邏輯:BFS用隊列實現“先進先出”,逐層掃描;DFS用棧(或遞歸)實現“后進先出”,縱深突破。

  • 適用場景:BFS適合最短路徑,DFS擅長深入探測結構特性(如回溯問題、圖的連通分量統計)。

  • 空間效率:BFS在稠密圖中可能內存暴增,DFS的空間消耗通常與路徑深度成正比,更適合樹形結構。

本文你將收獲:

  • DFS核心思想:從二叉樹的先序/后序遍歷,推演至圖的深度優先法則,圖解“一條路走到黑+回溯”的精髓。
  • 代碼與邏輯全解:遞歸與非遞歸實現對比,visited數組如何避免重復訪問,連通圖與非連通圖的遍歷陷阱。
  • 深度優先生成樹:如何用DFS“繪制”圖的骨架,鄰接矩陣與鄰接表為何導致生成樹不唯一?
  • 實戰思考:DFS在有向圖(如依賴解析)與無向圖中的不同表現,強連通分量的秘密。

閱讀建議:搭配上一篇“BFS詳解”食用更佳!通過對比兩大算法,你將真正掌握“何時用BFS,何時選DFS”的決策智慧。文末附生成樹案例詳解,幫助你將抽象理論轉化為直觀洞察。🚀

現在,讓我們一起潛入圖論的深海,揭開DFS的層層奧秘吧!

一、深度優先搜索

深度優先搜索(Depth-First-Search, DFS),簡單的理解就是盡可能深的進行遍歷,用一句話來描述就是一條路走到黑

在二叉樹的遍歷算法中,按照遍歷的方式,我們可以將其分為4類:

  • 先根遍歷:根—>左—>右
  • 中根遍歷:左—>根—>右
  • 后根遍歷:左—>右—>根
  • 層序遍歷:分層遍歷

其中層序遍歷實際上就是我們所說的:廣度優先搜索(BFS)在樹中的一種實際應用。在上一篇的內容中我們已經詳細介紹,這里就不再贅述。

下面我們就來分析一下其他的三種遍歷;

  • 先根遍歷的核心是:先訪問根結點,再訪問子樹,對應到圖中,就是先訪問當前頂點,再訪問與其鄰接的頂點;
  • 中根遍歷是二叉樹這種特殊的樹形結構獨有的一種遍歷方式,因此我們不能夠通過該遍歷方式來拓展到圖的遍歷中;
  • 后根遍歷的核心是:先訪問子樹,再訪問根結點,對應到圖中,就是先訪問與當前頂點鄰接的頂點,再訪問當前頂點;

不管是先根遍歷還是后根遍歷,其遍歷的方式都是沿著一條路徑先找到最深的結點,再去找其他結點。

對于先根遍歷與后根遍歷這種每次遍歷時都是沿著一條路徑,往深處走的方式進行遍歷,就是深度優先遍歷(Depth-First-Traversal, DFT)

當我們要查找具體的對象時,采用這種沿著一條路徑,往深處走的方式進行查找,這就是深度優先搜索(Depth-First-Search, DFS)

這里我們需要區分一下遍歷與搜索:

  • 遍歷簡單的理解就是無條件地對數據結構中的所有元素進行訪問;
  • 搜索簡單的理解就是有條件地對數據結構中的特定元素進行訪問;

由此可以看到,當所有元素都是搜索中的特定元素時,那么我們對存儲這些數據元素的數據結構進行搜索時,實際上就是在遍歷該數據結構。

因此我們可以簡單的理解為,遍歷與搜索的區別就是:對元素的訪問條件不同

深度優先遍歷(DFT)深度優先搜索(DFS) 是同一策略的不同應用場景,但術語上更常用 DFS 統稱。

這里一定要注意,在下面的介紹中,我們說的 DFS 實際上是說的對圖的遍歷算法,而不是查找特定值的算法。

理解了深度優先遍歷與深度優先搜索后,下面我們就來了解一下其算法思路;

二、算法思路

深度優先搜索的算法思路如下:

  • 首先訪問圖中某一起始頂點 v v v
  • 然后從 v v v 出發,訪問與 v v v 鄰接且未被訪問的任意一個頂點 w 1 w_1 w1?
  • 再訪問與 w 1 w_1 w1? 鄰接且未被訪問的人一個頂點 w 2 w_2 w2?
  • 重復上述過程,直到 w i w_i wi? 不存在與其鄰接且未被訪問的頂點
  • 當無法繼續訪問時,依次退回到最近被訪問的頂點
  • 當退回后的頂點存在還未被訪問的鄰接頂點,則從該頂點繼續上述搜索過程,直至所有頂點完成訪問

這里我們以二叉樹的先序遍歷為例:
深度優先搜索
上圖中使用的是先根遍歷的方式進行展示:

  • 二叉樹:先訪問根結點,再訪問子樹
  • 圖:先訪問當前頂點,再訪問當前頂點的鄰接頂點

對于圖而言,其遍歷序列根據其存儲結構的不同而有所不同:

  • 鄰接矩陣:同一起始頂點的遍歷序列相同
  • 鄰接表:同一起始頂點的遍歷序列不同
  • 十字鏈表:同一起始點的遍歷序列不同
  • 鄰接多重表:同一起始點的遍歷序列不同

上圖所示的遍歷序列對于除鄰接矩陣外的存儲結構而言,只是其中的一種遍歷序列,僅供大家參考。

三、算法邏輯

今天我們要介紹的圖的深度優先搜索與樹的先根遍歷類似,都是先訪問當前頂點,再對一條路徑進行深入,直至該路徑無法繼續深入后,開始回溯,選擇下一條路徑進行深入;

在圖的 DFS 中我們需要對已經完成訪問的頂點進行標記,因此需要一個標記數組 visited[] 來記錄當前頂點是否被訪問。整個過程如下所示:

  • 訪問當前起始頂點,并對起始頂點進行標記
  • 通過 FirstNeighbor(G, x) 獲取當前頂點x的下一個鄰接頂點編號y,并通過 visited[y] 進行判斷該頂點是否被訪問:
    • visited[y] == false 則未被訪問,繼續對頂點y進行 DFS
    • visited[y] == true 則已被訪問,則說明該路徑上的頂點都已被訪問,接下來通過 NexttNeighbor(G, x, y) 獲取頂點x除了頂點y之外的下一個鄰接頂點編號z,并對該頂點進行判斷是否被訪問:
      • 存在未被訪問的頂點,繼續對該頂點進行 DFS
      • 不存在未被訪問的頂點,開始回溯

其代碼表達如下所示:

// 深度優先搜索
bool visited[MAXVERSIZE];
void DFS(graph* g, int x) {visit(g, x);					// 訪問當前頂點visited[x] = true;				// 標記當前頂點for (int y = FirstNeighbor(g, x); y >= 0; y = NextNeighbor(g, x, y)) {// FirstNeighbor(g, x): 當前頂點x存在下一個鄰接點,則返回對應頂點編號,否則,返回-1// NextNeighbor(g, x, y): 當前頂點x存在下一個除頂點y以外的鄰接點,則返回對應頂點編號,否則,返回-1// y == -1時,說明此時該路徑中不存在未被訪問的鄰接點if (!visited[y]) {			// 判斷當前頂點是否被訪問DFS(g, y);				// 未被訪問,則對該點進行深度優先搜索}}
}

對于連通圖而言,上述代碼邏輯足以完成所有頂點的遍歷,但是在非連通圖中,從某一起始點開始進行 DFS 只能完成該點所在連通分量的所有頂點的遍歷。

為了確保能夠對非連通圖完成所有頂點的遍歷,我們需要借助 visited[] 數組來查找未被訪問過的頂點信息,如下所示:

void DFSTraverse(graph* g) {// 初始化標記數組for (int i = 0; i < MAXVERSIZE; i++) {visited[i] = false;}for (int i = 0; i < MAXVERSIZE; i++) {if (!visited[i]) {DFS(g, i);}}
}

在該函數中,每一次調用 DFS 就是對圖中的一個連通分量進行遍歷,圖中存在多少個連通分量,就會調用多少次 DFS

四、算法評價

圖的遍歷算法的本質就是通過邊來找頂點,因此對于 DFS 而言,其時間復雜度與 BFS 的時間復雜度一致:

  • 鄰接矩陣: O ( ∣ V ∣ 2 ) O(|V|^2) O(V2)
  • 鄰接表/十字鏈表/鄰接多重表: O ( ∣ V ∣ + ∣ E ∣ ) O(|V| + |E|) O(V+E)

DFS 中,我們可以像上述展示的代碼一樣,通過遞歸實現,其對應的空間復雜度為: O ( ∣ V ∣ ) O(|V|) O(V)

同樣也可以通過棧的方式來實現,對應的空間復雜度依然是: O ( ∣ V ∣ ) O(|V|) O(V)

五、深度優先生成樹

與廣度優先生成樹一致,深度優先生成樹也是在遍歷圖的過程中保留所有的頂點與其訪問的邊所得到的一棵生成樹,我們以下面的例子來說明:

a
b
d
c
e
f
g

在上圖中,其頂點集與邊集如下所示:

  • 頂點集: V = { a , b , c , d , e , f , g } V = \{a, b, c, d, e, f, g\} V={a,b,c,d,e,f,g}
  • 邊集: E = { ( a , b ) , ( a , c ) , ( b , d ) , ( b , e ) , ( c , e ) , ( e , f ) , ( e , g ) , ( f , g ) } E = \{(a, b), (a, c), (b, d), (b, e), (c, e), (e, f), (e, g), (f, g)\} E={(a,b),(a,c),(b,d),(b,e),(c,e),(e,f),(e,g),(f,g)}

當我們從起始點 a 開始進行遍歷時,此時點a 會被標記,其對應的生成樹為:

a

對于點a而言,其鄰接點有兩個:

  • 點b:未訪問
  • 點c:未訪問

當我們找到第一個鄰接點b時,點b會被標記,所對應的邊 ( a , b ) (a, b) (a,b) 被訪問,對應的生成樹為:

a
b

對于點b而言,其鄰接點有3個:

  • 點a:已訪問
  • 點d:未訪問
  • 點e:未訪問

這時找到的鄰接點a已經被訪問,算法會繼續尋找除了點a外的下一個鄰接點d。

當找到點d后,點d會被標記,所對應的邊 ( b , d ) (b, d) (b,d) 被訪問,其對應的生成樹為:

a
b
d

對于點d而言,其鄰接點有1個:

  • 點b:被訪問

由于點d不存在未被訪問的鄰接點,算法會開始回溯到點b,這時會繼續尋找與點b鄰接的下一個鄰接點e。

當找到點e后,點e會被標記,所對應的邊 ( b , e ) (b, e) (b,e) 被訪問,對應的生成樹為:

a
b
d
e

對于點e而言,其鄰接點有4個:

  • 點b:已訪問
  • 點c:未訪問
  • 點f:未訪問
  • 點g:未訪問

這時算法會重復上述的步驟依次找到并標記以下頂點與邊:

  • 頂點:c,對應邊: ( e , c ) (e, c) (e,c)
  • 頂點:f,對應邊: ( e , f ) (e, f) (e,f)
  • 頂點:g,對應邊: ( f , g ) (f, g) (f,g)

此時所有的頂點都完成了標記,我們也就得到了該圖的深度優先生成樹:

a
b
d
e
c
f
g

深度優先生成樹在不同的存儲結構中,同樣不相同:

  • 鄰接矩陣:深度優先生成樹唯一
  • 鄰接表/十字鏈表/鄰接多重表:深度優先生成樹不唯一

具體的原因我這里再重復一遍:

  • 在鄰接表/十字鏈表/鄰接多重表中,邊的存儲是以鏈表的形式進行存儲,因此結點的位置可能發生變化,因此對應的生成樹也會發生變化

六、有向圖與無向圖

現在我們已經了解了圖的兩種遍歷方式:BFSDFS ,不管是哪種方式,在對無向圖進行遍歷與對有向圖進行遍歷時,是有些許區別的:

  • 在無向圖中,兩種遍歷方式的調用次數 = 連通分量的數量
  • 在有向圖中,兩種遍歷方式的調用次數都需要根據實際情況進行分析:
    • 起始點到其它頂點都有路徑,則只需調用一次
    • 起始點與其它的頂點之間不存在路徑,則需多次調用
    • 有向圖為強連通圖,無論從哪個頂點出發,都只需要調用一次

在這兩個篇章中我們都是以無向圖為例進行說明,但是在實際問題中,我們還是需要根據具體情況進行具體分析。

結語:深潛與回溯,揭開圖論世界的另一面

通過本篇的探索,我們見證了深度優先搜索(DFS)如何以“不撞南墻不回頭”的執著,在圖結構中開辟出一條條縱深路徑。與廣度優先搜索(BFS)的“層層遞進”不同,DFS以遞歸與回溯為利器,在迷宮尋路、拓撲排序、連通分量統計等場景中展現獨特優勢。

關鍵回顧🔍

  • DFS的核心邏輯:從二叉樹的遍歷(先序/后序)出發,推演至圖的深度探索策略,通過visited數組避免重復訪問,用遞歸或棧實現“一條路走到黑”的縱深突破。

  • 生成樹的多樣性:DFS生成樹的形態因存儲結構(鄰接矩陣 vs 鄰接表)而異,揭示了算法執行路徑的不確定性,也體現了圖遍歷的靈活性。

  • 場景適應性

    • 無向圖:DFS調用次數=連通分量數,天然適合檢測圖的連通性。

    • 有向圖:強連通分量需特殊處理,DFS在依賴解析、環路檢測中表現卓越。

實踐啟示💡

  • 代碼實現:遞歸簡潔但需警惕棧溢出,非遞歸棧實現更適合大規模圖。

  • 性能權衡:鄰接表下DFS時間復雜度為 O ( V + E ) O(V + E) O(V+E),空間復雜度與遞歸深度正相關,樹形圖優化顯著。

  • 決策智慧:遇到回溯問題、連通分量分析時優先考慮DFS;追求最短路徑或層序關系時轉向BFS。

如果本文對你有所啟發,歡迎互動支持!
👉 關注👆蒙奇D索大,獲取更多算法深度解析
👉 點贊👍鼓勵持續創作
👉 收藏📁備查實戰應用
👉 轉發📤分享給更多同行

圖論的海洋浩瀚無垠,DFS與BFS僅是探索的起點。下一篇我們將深入《圖的實際應用——最小生成樹(Minimum Spanning Tree)》,揭秘如何在復雜網絡中高效構建成本最低的連通骨架,無論是Kruskal的貪心策略,還是Prim的頂點擴展法,都將為你打開優化問題的新視野。敬請期待,一起用算法編織智慧之網! 🌟

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

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

相關文章

Flink CEP實踐總結:使用方法、常見報錯、優化與難點應對

Flink CEP實踐總結&#xff1a;使用方法、常見報錯、優化與難點應對 隨著實時數據分析需求的提升&#xff0c;Flink CEP&#xff08;Complex Event Processing&#xff0c;復雜事件處理&#xff09;成為事件流檢測中的利器。本文結合實際項目經驗&#xff0c;總結Flink CEP的基…

Python數據類型詳解:從字符串到布爾值,一網打盡

Python是現代編程語言中非常流行的一種&#xff0c;它的語法簡潔、易懂&#xff0c;非常適合初學者。而在Python編程中&#xff0c;“數據類型”是最基礎也是最重要的概念。理解這個概念&#xff0c;將為你之后的編程打下堅實的基礎。 1. 什么是數據類型&#xff1f; 在Pytho…

python打卡day42

Grad-CAM與Hook函數 知識點回顧 回調函數lambda函數hook函數的模塊鉤子和張量鉤子Grad-CAM的示例 在深度學習中&#xff0c;我們經常需要查看或修改模型中間層的輸出或梯度&#xff0c;但標準的前向傳播和反向傳播過程通常是一個黑盒&#xff0c;很難直接訪問中間層的信息。PyT…

中國風展示工作總結商務通用PPT模版

中國風展示工作總結商務通用PPT模版&#xff1a;中國風商務通用PPT 模版https://pan.quark.cn/s/42ad18c010d4

TeleAI發布TeleChat2.5及T1正式版,雙雙開源上線魔樂社區!

5月12日&#xff0c;中國電信開源TeleChat系列四個模型&#xff0c;涵蓋復雜推理和通用問答的多個尺寸模型&#xff0c;包括TeleChat-T1-35B、TeleChat-T1-115B、TeleChat2.5-35B和TeleChat2.5-115B&#xff0c;實測模型性能均有顯著的性能效果。TeleChat系列模型基于昇思MindS…

機器視覺2D定位引導一般步驟

機器視覺的2D定位引導是工業自動化中的核心應用,主要用于精確確定目標物體的位置(X, Y坐標)和角度(旋轉角度θ),并引導機器人或運動機構進行抓取、裝配、對位、檢測等操作。其一般步驟可概括如下: 一、系統規劃與硬件選型 明確需求: 定位精度要求(多少毫米/像素,多少…

兒童節快樂,聊聊數字的規律和同余原理

某年的6月1日是星期日。那么&#xff0c;同一年的6月30日是星期幾&#xff1f; 星期是7天一個循環。所以說&#xff0c;這一天是星期幾&#xff0c;7天之后同樣也是星期幾。而6月30日是在6月1日的29天之后&#xff1a;29 7 4 ... 1用29除以7&#xff0c;可以得出余數為1。而…

最佳實踐|互聯網行業軟件供應鏈安全建設的SCA縱深實踐方案

在數字化轉型的浪潮中&#xff0c;開源組件已成為企業構建云服務與應用的基石&#xff0c;但其引入的安全風險也日益凸顯。某互聯網大廠的核心安全研究團隊&#xff0c;通過深度應用軟件成分分析&#xff08;SCA&#xff09;技術&#xff0c;構建了一套覆蓋開源組件全生命周期管…

Docker Compose(容器編排)

目錄 什么是 Docker Compose Docker Compose 的功能 Docker Compose 使用場景 Docker Compose 文件&#xff08;docker-compose.yml&#xff09; Docker Compose 命令清單 常見命令說明 操作案例 總結 什么是 Docker Compose docker-compose 是 Docker 官方的開源項…

【網絡安全】輕量敏感路徑掃描工具

訂閱專欄,獲取文末項目源碼。 文章目錄 工具簡介工具特點項目結構使用方法1.環境準備2.配置目標URL3.運行掃描4.結果查看5.自定義擴展項目源碼工具簡介 該工具是一款基于Python的異步敏感路徑掃描工具,用于檢測目標網站是否存在敏感文件或路徑泄露(如配置文件、密鑰、版本控…

SpringAI+DeepSeek大模型應用開發實戰

內容來自黑馬程序員 這里寫目錄標題 認識AI和大模型大模型應用開發模型部署方案對比模型部署-云服務模型部署-本地部署調用大模型什么是大模型應用傳統應用和大模型應用大模型應用 大模型應用開發技術架構 SpringAI對話機器人快速入門會話日志會話記憶 認識AI和大模型 AI的發…

高溫爐制造企業Odoo ERP實施規劃與深度分析報告

摘要 本報告旨在為高溫爐生產企業提供一個基于Odoo 18平臺的企業資源規劃&#xff08;ERP&#xff09;系統實施的全面分析與規劃。報告首先系統梳理了高溫爐制造業獨特的業務流程特點&#xff0c;隨后詳細映射了Odoo 18各核心模塊功能與這些業務需求的匹配程度。重點分析了生產…

簡述什么是全局鎖?它的應用場景有哪些?

全局鎖是數據庫管理系統中的一種特殊鎖機制&#xff0c;用于對整個數據庫實例進行加鎖&#xff0c;使數據庫處于只讀狀態&#xff0c;阻止所有數據更新&#xff08;DML&#xff09;、數據定義&#xff08;DDL&#xff09;及更新類事務提交等操作。 其核心應用場景包括&#xf…

window 顯示驅動開發-呈現開銷改進(二)

對共享表面的紋理格式支持 驅動程序應支持共享資源和可共享的后臺緩沖區&#xff0c;以使用 DXGI_FORMAT 枚舉中的這些附加紋理格式&#xff1a; DXGI_FORMAT_A8_UNORMDXGI_FORMAT_R8_UNORMDXGI_FORMAT_R8G8_UNORMDXGI_FORMAT_BC1_TYPELESS\*DXGI_FORMAT_BC1_UNORMDXGI_FORMAT…

jenkins集成gitlab實現自動構建

jenkins集成gitlab實現自動構建 前面我們已經部署了Jenkins和gitlab&#xff0c;本文介紹將二者結合使用 項目源碼上傳至gitee提供公網訪問&#xff1a;https://gitee.com/ye-xiao-tian/my-webapp 1、創建一個群組和項目 2、添加ssh密鑰 #生成密鑰 [rootgitlab ~]# ssh-keyge…

barker-OFDM模糊函數原理及仿真

文章目錄 前言一、巴克碼序列二、barker-OFDM 信號1、OFDM 信號表達式2、模糊函數表達式 三、MATLAB 仿真1、MATLAB 核心源碼2、仿真結果①、barker-OFDM 模糊函數②、barker-OFDM 距離分辨率③、barker-OFDM 速度分辨率④、barker-OFDM 等高線圖 四、資源自取 前言 本文進行 …

深入解析 Redis Cluster 架構與實現(一)

#作者&#xff1a;stackofumbrella 文章目錄 Redis Cluster特點Redis Cluster與其它集群模式的區別集群目標性能hash tagsMutli-key操作Cluster Bus安全寫入&#xff08;write safety&#xff09;集群節點的屬性集群拓撲節點間handshake重定向與reshardingMOVED重定向ASK重定向…

linux centos 服務器性能排查 vmstat、top等常用指令

背景:項目上經常出現系統運行緩慢,由于數據庫服務器是linux服務器,記錄下linux服務器性能排查常用指令 vmstat vmstat介紹 vmstat 命令報告關于內核線程、虛擬內存、磁盤、陷阱和 CPU 活動的統計信息。由 vmstat 命令生成的報告可以用于平衡系統負載活動。系統范圍內的這…

在IIS上無法使用PUT等請求

錯誤來源&#xff1a; chat:1 Access to XMLHttpRequest at http://101.126.139.3:11000/api/receiver/message from origin http://101.126.139.3 has been blocked by CORS policy: No Access-Control-Allow-Origin header is present on the requested resource. 其實我的后…

Python訓練第四十一天

DAY 41 簡單CNN 知識回顧 數據增強卷積神經網絡定義的寫法batch歸一化&#xff1a;調整一個批次的分布&#xff0c;常用與圖像數據特征圖&#xff1a;只有卷積操作輸出的才叫特征圖調度器&#xff1a;直接修改基礎學習率 卷積操作常見流程如下&#xff1a; 1. 輸入 → 卷積層 →…