【數據結構】圖論最短路徑算法深度解析:從BFS基礎到全算法綜述?

最短路徑

  • 導讀
  • 一、最短路徑
    • 1.1 單源最短路徑
    • 1.2 各頂點間的最短路徑
    • 1.3 最短路徑算法
  • 二、BFS算法
  • 結語
    • 內容回顧
    • 下一篇預告:挑戰帶權最短路徑!

最短路徑

導讀

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

歡迎繼續探索圖算法的精彩世界!在上一篇博客中,我們研究了最小生成樹(MST)問題——它專注于為整個連通圖尋找一棵連接所有頂點且總權重最小的“骨架樹”,就像鋪設覆蓋整個城市且成本最低的電網。

今天,我們將目光轉向一個同樣關鍵但目標不同的單點決策問題:最短路徑。想象你站在地圖上的一個特定起點(比如“家”),目標是以最小的累計代價(時間、距離、費用)到達圖中另一個點或所有其他點。這就是最短路徑問題的核心!

最短路徑 vs. 最小生成樹

  • MST追求的是全局最優連接(覆蓋所有點)。
  • 最短路徑追求的是點到點的最優路徑(關注起點到特定終點的序列)。

本篇核心內容: 我們將

  • 清晰理解最短路徑的基本概念和問題分類(單源SSSP、所有頂點對APSP)。

  • 簡要羅列解決這些問題的經典算法(如Dijkstra用于非負權、Floyd用于所有頂點對)。

  • 重點探討第一種實戰場景:如何利用 BFS(廣度優先搜索)算法,高效求解非帶權圖(邊權視為1) 中的單源最短路徑問題——即從起點出發,到達圖中任一點的最少邊數/跳數路徑。

  • 深入理解 BFS用于 最短路徑的核心思路 和 C語言實現細節。

你是否好奇原本用于圖遍歷的BFS,如何巧變身為求解最短路徑的神器?它能否處理帶權重的復雜道路?這些問題的答案,將在正文中一一揭曉。讓我們立刻開始這段從起點尋找最優路線的旅程!

一、最短路徑

要理解什么是最短路徑,這里我們以一個比較形象的例子來進行理解:

5
3
3
8
12
5
學校
食堂
網吧

在這個地圖中,如果我們從家里出發要去學校的話,我們有多條路徑可供選擇,這里我們例舉部分路徑:

  • 家->學校:該路徑代價為5min
  • 家->食堂->學校:該路徑代價為6min
  • 家->網吧->學校:該路徑代價為20min
  • 家->網吧->食堂->學校:該路徑代價為16min
  • ……

從這些路徑中我們不難發現我們直接選擇:家->學校這條路徑所花費的代價是最小的,因此這條路徑就是最短路徑。

下面我們就來了解以下最短路徑的定義:

最短路徑:?? 指在起點和終點之間??所有可能路徑中??,其??邊上權重總和最小的??那條路徑。??路徑是否最短取決于你如何定義邊的權重??(是距離、時間還是成本?)。

問題本質是:在圖(由點和連接點的線組成)中,以某種代價(距離、時間、費用等)為衡量標準,尋找從起點到終點的最優路線,使得累計代價最小。??

1.1 單源最短路徑

單源最短路徑??(Single-Source Shortest Path, SSSP)是圖論中的核心問題,旨在解決:??給定一個帶權圖和指定的起點(源點),計算從該起點到圖中所有其他頂點的最短路徑及其距離??。

不局限于單個終點,而是生成一張覆蓋全圖的“最短路徑地圖”,為后續任意終點查詢提供基礎。

1.2 各頂點間的最短路徑

所有頂點間的最短路徑All-Pairs Shortest Paths, APSP)?? 是圖論中的一個核心問題,其目標是計算給定帶權圖中??每一對頂點之間??的最短路徑及其距離

簡單來說,就是求解圖中??任意起點??到??任意終點??最短路徑問題

1.3 最短路徑算法

在最短路徑問題中,主要有以下算法用于解決最短路徑問題:

  • BFS廣度優先搜索

    • 適用場景:無權圖(邊權=1),求最少邊數路徑(最小跳數)。
    • 時間復雜度 O ( ∣ V ∣ + ∣ E ∣ ) O(|V|+|E|) O(V+E)
    • 核心思想:按層次遍歷圖,首次到達終點的路徑即最短路徑。
  • Dijkstra 算法

    • 適用場景:非負權重圖(有向/無向)。
    • 時間復雜度
      • 樸素實現 O ( ∣ V ∣ 2 + ∣ E ∣ ) O(|V|^2 + |E|) O(V2+E)
      • 堆優化主流): O ( ( ∣ V ∣ + ∣ E ∣ ) l o g ∣ V ∣ ) O((|V|+|E|)log|V|) O((V+E)logV)
    • 核心思想:貪心策略 + 優先隊列。每次從未確定點中選取距離起點最近的頂點,松弛其鄰居。
    • 關鍵點
      • 需用優先隊列(最小堆) 加速。
      • 不能處理負權邊(可能導致錯誤結果)。
  • Bellman-Ford 算法

    • 適用場景:含負權邊圖(無負環),可檢測負權環。
    • 時間復雜度 O ( ∣ V ∣ ∣ E ∣ ) O(|V||E|) O(V∣∣E)
    • 核心思想: 進行 ∣ V ∣ ? 1 |V|-1 V?1 輪松弛操作(每輪遍歷所有邊),第 ∣ V ∣ |V| V 輪檢測負環。
  • A 算法*

    • 適用場景:非負權重圖 + 有明確終點 + 啟發函數(加速搜索)。
    • 時間復雜度:取決于啟發函數質量(最壞仍 O ( ( ∣ V ∣ + ∣ E ∣ ) l o g ∣ V ∣ ) O((|V|+|E|)log|V|) O((V+E)logV))。
    • 核心思想:擴展 Dijkstra,引入啟發函數 h ( v ) h(v) h(v)(估算 v v v 到終點的代價),優先擴展 f ( v ) = g ( v ) + h ( v ) f(v)=g(v)+h(v) f(v)=g(v)+h(v) 最小的點( g ( v ) g(v) g(v) 為起點到 v v v 的實際代價)。
    • 要求 h ( v ) h(v) h(v) 必須為可采納啟發函數(不能高估真實代價)。
    • 應用:游戲尋路、機器人導航(如 h ( v ) h(v) h(v) 用歐式距離)。
  • Floyd-Warshall 算法

    • 適用場景:所有頂點對最短路徑(APSP),支持負權邊(無負環)。
    • 時間復雜度 O ( ∣ V ∣ 3 ) O(|V|^3) O(V3)
    • 核心思想: 動態規劃。定義 d i s t [ i ] [ j ] dist[i][j] dist[i][j] 為通過頂點 1.. k 1..k 1..k 中轉時 i → j i→j ij 的最短距離,迭代更新 k k k
    • 核心公式 d i s t [ i ] [ j ] = m i n ( d i s t [ i ] [ j ] , d i s t [ i ] [ k ] + d i s t [ k ] [ j ] ) dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]) dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j])
  • Johnson 算法(高效處理帶負權的 APSP)

    • 適用場景:含負權邊的稀疏圖(無負環)的 APSP 問題。
    • 時間復雜度 O ( ∣ V ∣ ∣ E ∣ l o g ∣ V ∣ ) O(|V||E| log |V|) O(V∣∣ElogV) (優于 Floyd 在稀疏圖的表現)
    • 步驟
      • 添加虛擬節點 s s s,連接所有頂點(邊權=0)。
      • 用 Bellman-Ford 計算 s s s 到各點的最短距離 h ( v ) h(v) h(v)(勢能函數)。
      • 重加權邊: w ^ ( u , v ) = w ( u , v ) + h ( u ) ? h ( v ) ?(u,v) = w(u,v) + h(u) - h(v) w^(u,v)=w(u,v)+h(u)?h(v)(新權重非負)。
      • 對每個頂點運行 Dijkstra(基于 w ^ ? w^)。
      • 還原真實距離: d i s t ( u , v ) = d i s t ^ ( u , v ) ? h ( u ) + h ( v ) dist(u,v) = dist?(u,v) - h(u) + h(v) dist(u,v)=dist^(u,v)?h(u)+h(v)

各算法對應的應用場景如下所示:

場景需求推薦算法
無權圖(最少邊數)BFS
非負權圖(單源最短路徑)Dijkstra
含負權邊(單源,需檢測負環)Bellman-Ford
非負權圖 + 終點明確 + 需加速A*
小規模圖或稠密圖(所有點對最短路徑)Floyd-Warshall
含負權的稀疏圖(所有點對)Johnson

在現階段,我們主要需要了解3種算法:

  • 單源路徑算法(SSSP):
    • BFS算法
    • Dijkstra算法
  • 各頂點間的最短路徑(APSP):
    • Floyd算法

二、BFS算法

BFS算法用于解決非帶權圖的單源最短路徑問題。所謂的非帶權圖,我們可以視作各邊權值為1的特殊帶權圖。

在之前我們有介紹過廣度優先生成樹,整個生成樹的創建過程實際上就是由近到遠的圖遍歷過程:

  • 生成樹的根結點就是單源最短路徑中的起點(源點)
  • 從根結點到各結點的路徑就是單源最短路徑中起點到各結點的最短路徑

因此整個算法的實現我們可以參考廣度優先生成樹的算法實現:

  • 通過visited[]數組來記錄當前頂點是否被訪問
  • 通過d[]數組來記錄當前頂點到源點的最短路徑
  • 通過path[]數組來記錄當前頂點的前驅頂點
  • 通過隊列queue[]來實現整個遍歷過程
  • 算法的核心邏輯為 BFS
  • 對未訪問頂點的處理:
    • 通過d[]來記錄當前頂點到原點的路徑長度
    • 通過path[]來記錄當前頂點的前驅頂點
    • 通過visited[]來更改當前頂點的訪問狀態

對于BFS算法的核心邏輯,我們在廣度優先遍歷的篇章中已經詳細介紹,這里就不再展開,感興趣的朋友可以點擊鏈接閱讀相關內容。

下面我們直接來看C語言代碼:

int visited[MAXVERSIZE];				// 遍歷標記數組
int d[MAXVERSIZE];						// 路徑長度數組
int path[MAXVERSIZE];					// 路徑前驅數組
void BFS_MIN_Distance(graph* g, int u) {for (int i = 0; i < MAXVERSIZE; i++) {visited[i] = false;				// 初始化所有頂點為未訪問狀態d[i] = -1;						// 初始化頂點與源點不存在路徑path[i] = -1;					// 初始化所有頂點沒有前驅頂點}visited[u] = true;					// 訪問源點d[u] = 0;							// 源點距離源點路徑長度為0path[u] = -1;						// 源點不存在前驅頂點Queue* Q = CreatQueue();			// 遍歷隊列EnQueue(Q, u);						// 源點入隊while (!IsEmpty(Q)) {int p = DeQueue(Q);				// 隊首元素出隊for (int w = FirstNeighbor(g, p); w >= 0; w = NextNeighbor(g, p, w)) {if (!visited[w]) {visited[w] = true;		// 訪問當前頂點path[w] = p;			// 當前頂點的前驅頂點為pd[w] = d[p] + 1;		// 當前頂點距離源點的距離為頂點p距離源點的距離+1EnQueue(Q, w);			// 當前頂點入隊}}}DestroyQueue(&Q);					// 銷毀隊列
}

BFS 求解單源最短路徑問題的思路很簡單,只需要在廣度優先生成樹的基礎上額外維護兩個數組用于記錄頂點距離源點的最短路徑和其前驅頂點即可。

由于解決的是非帶權圖的單源最短路徑問題,因此我們只需要對圖完成最基本的BFS即可,整個算法的時間復雜度為: O ( ∣ V ∣ + ∣ E ∣ ) O(|V|+|E|) O(V+E)

上述算法是在連通圖中求解單源最短路徑問題,如果是在非連通圖中,我們可以通過遍歷visited[]來找到未被訪問的頂點。這個問題在廣度優先遍歷的章節中同樣有過介紹,這里我就不再贅述。

結語

內容回顧

通過本篇博客,我們一起深入探討了最短路徑問題的核心概念與應用場景。主要內容總結如下:

  • 最短路徑問題本質

    • 在帶權圖中尋找起點到終點之間累計權重最小的路徑(如最短時間、最短距離或最小成本路徑),是解決實際路線規劃問題的理論基礎。
  • 問題分類與算法全景

    • 單源最短路徑(SSSP):從指定起點到所有其他頂點的最短路徑(如導航起點到全城地點)。
    • 所有頂點間的最短路徑(APSP):計算圖中任意兩點間的最短路徑(如城市交通網全局分析)。
  • 簡要介紹了六類主流算法BFSDijkstraBellman-FordA*FloydJohnson及其適用場景

  • BFS算法的實戰應用

    • 重點解析了如何利用廣度優先搜索(BFS)高效解決無權圖的最短路徑問題:
      • 將無權圖視為邊權均為1的特殊帶權圖,通過 d[]數組記錄距離、path[]數組回溯路徑
      • 基于層次遍歷特性,首次訪問的路徑即為最短路徑,時間復雜度僅 O ( V + E ) O(V + E ) O(V+E)
      • 提供完整C語言實現代碼,適用于網絡路由、社交關系等最小跳數應用場景

下一篇預告:挑戰帶權最短路徑!

至此,我們掌握了無權圖的最短路徑解決方案。但當圖中路徑的代價不再均等(如公路有長短、任務有耗時)時,BFS就無法滿足需求了!

下一篇博客將聚焦兩類核心算法:
🔹 Dijkstra算法

  • 解決帶非負權重圖的單源最短路徑問題,用貪心策略+優先隊列實現高效搜索。你將學到:
    • 如何通過 “松弛操作” 動態更新最短路徑
    • 堆優化實現細節與時間復雜度分析
    • 為何它無法處理負權邊

🔹 Floyd算法

  • 一網打盡所有頂點間的最短路徑(APSP),動態規劃的經典應用:
    • 三重循環背后的子問題分解思想
    • 中轉頂點k的迭代優化邏輯
    • 負權環的檢測方法

代碼實戰預告:下一篇將提供兩類算法的C語言實現,手把手帶你寫出高效的最短路徑計算器!

覺得這篇博客有幫助? ?? 您的支持是我持續創作的動力!
👉 關注我,第一時間獲取技術干貨更新
👍 點贊收藏,方便日后復習查找
💬 評論提問或分享你的見解,我們一起探討
🔄 轉發給需要的朋友,共同進步!

下一站,我們將在加權圖的復雜道路中尋找最優解——DijkstraFloyd算法深度解析,不見不散!

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

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

相關文章

中國政務數據安全建設細化及市場需求分析

(基于新《政務數據共享條例》及相關法規) 一、引言 近年來,中國政府高度重視數字政府建設和數據要素市場化配置改革。《政務數據共享條例》(以下簡稱“《共享條例》”)的發布,與《中華人民共和國數據安全法》(以下簡稱“《數據安全法》”)、《中華人民共和國個人信息…

Linux信號保存與處理機制詳解

Linux信號的保存與處理涉及多個關鍵機制&#xff0c;以下是詳細的總結&#xff1a; 1. 信號的保存 進程描述符&#xff08;task_struct&#xff09;&#xff1a;每個進程的PCB中包含信號相關信息。 pending信號集&#xff1a;記錄已到達但未處理的信號&#xff08;未決信號&a…

【Redis】筆記|第10節|京東HotKey實現多級緩存架構

緩存架構 京東HotKey架構 代碼結構 代碼詳情 功能點&#xff1a;&#xff08;如代碼有錯誤&#xff0c;歡迎討論糾正&#xff09; 多級緩存&#xff0c;先查HotKey緩存&#xff0c;再查Redis&#xff0c;最后才查數據庫熱點數據重建邏輯使用分布式鎖&#xff0c;二次查詢更新…

php apache構建 Web 服務器

虛擬機配置流程winsever2016配置Apache、Mysql、php_windows server 2016配置web服務器-CSDN博客 PHP 和 Apache 通過 ??模塊化協作?? 共同構建 Web 服務器&#xff0c;以下是它們的交互機制和工作流程&#xff1a; ??一、核心組件分工?? 組件角色??Apache??Web …

二分查找排序講解

一、二分查找&#xff08;Binary Search&#xff09; 核心思想&#xff1a; 前提&#xff1a;數組必須是 有序的&#xff08;比如從小到大排列&#xff09;。目標&#xff1a;在數組中快速找到某個數&#xff08;比如找 7&#xff09;。方法&#xff1a;每次排除一半的數&…

【Redis實戰:緩存與消息隊列的應用】

在現代互聯網開發中&#xff0c;Redis 作為一款高性能的內存數據庫&#xff0c;廣泛應用于緩存和消息隊列等場景。本文將深入探討 Redis 在這兩個領域的應用&#xff0c;并通過代碼示例比較兩個流行的框架&#xff08;Redis 和 RabbitMQ&#xff09;的特點與適用場景&#xff0…

[拓撲優化] 1.概述

常見的拓撲優化方法有&#xff1a;均勻化法、變密度法、漸進結構優化法、水平集法、移動可變形組件法等。 常見的數值計算方法有&#xff1a;有限元法、有限差分法、邊界元法、離散元法、無網格法、擴展有限元法、等幾何分析等。 將上述數值計算方法與拓撲優化方法結合&#…

【openssl】升級為3.3.1,避免安全漏洞

本文檔旨在形成 對Linux系統openssl版本進行升級 的搭建標準操作過程&#xff0c;搭建完成后&#xff0c;實現 openssl 達到3.3以上版本&#xff0c;避免安全漏洞 效果。 一、查看當前版本 版本不高于3.1的&#xff0c;均需要升級。 # 服務器上運行以下命令&#xff0c;查看…

基于正點原子阿波羅F429開發板的LWIP應用(6)——SNTP功能和lwiperf測速

說在開頭 正點原子F429開發板主芯片采用的是STM32F429IGT6&#xff0c;網絡PHY芯片采用的是LAN8720A(V1)和YT8512C(V2)&#xff0c;采用的是RMII連接&#xff0c;PHY_ADDR為0&#xff1b;在代碼中將會對不同的芯片做出適配。 CubeMX版本&#xff1a;6.6.1&#xff1b; F4芯片組…

C:\Users\中文名修改為英文名

C:\Users\中文名修改為英文名 背景操作步驟 背景 買了臺新電腦&#xff0c;初始化好不知道啥操作把自己的登錄用戶名改成了中文&#xff0c;有些安裝的軟件看見有中文直接就水土不服了。 操作步驟 以下稱中文用戶名為張三。 正常登錄張三用戶 進入用戶管理頁面修改用戶名&a…

YOLOv12環境配置,手把手教你使用YOLOv12訓練自己的數據集和推理(附YOLOv12網絡結構圖),全文最詳細教程

文章目錄 前言一、YOLOv12代碼下載地址1.YOLOv12模型結構圖 二、YOLO環境配置教程1.創建虛擬環境2.激活虛擬環境3.查詢自己電腦可支持最高cuda版本是多少&#xff08;無顯卡的同學可以跳過這個步驟&#xff09;4.pytorch安裝5.驗證 PyTorch GPU 是否可用&#xff08;沒有顯卡的…

ES6(ES2015)特性全解析

ES6&#xff08;ECMAScript 2015&#xff09;是 JavaScript 語言發展史上的一個重要里程碑&#xff0c;它引入了許多新的語法特性和功能&#xff0c;提升了代碼的可讀性、可維護性和開發效率。 1. 塊級作用域變量&#xff1a;let 和 const ES6 引入了 let 和 const 關鍵字&am…

jvm 垃圾收集算法 詳解

垃圾收集算法 分代收集理論 垃圾收集器的理論基礎&#xff0c;它建立在兩個分代假說之上&#xff1a; 弱分代假說&#xff1a;絕大多數對象都是朝生夕滅的。強分代假說&#xff1a;熬過越多次垃圾收集過程的對象就越難以消亡。 這兩個分代假說共同奠定了多款常用的垃圾收集…

數字孿生+AR/VR的融合創新

目錄 引言&#xff1a;工業元宇宙的興起與技術基石數字孿生&#xff1a;工業元宇宙的數字底座 2.1 數字孿生的概念與關鍵要素 2.2 數字孿生在工業領域的應用 2.3 數字孿生的技術架構 (Mermaid Graph) AR/VR&#xff1a;工業元宇宙的沉浸式體驗層 3.1 AR/VR 的概念與技術原理…

圖解C#教程 第五版 第4章 類型、存儲和變量 筆記

第4章 類型、存儲和變量 筆記 4.1 C# 程序是一組類型聲明 C程序是一組函數和數據類型&#xff0c;C程序是一組函數和類&#xff0c; 而C#程序是一組類型聲明&#xff0c;具有如下特征&#xff1a; C# 程序或 DLL 的源代碼是一組類型聲明類型聲明中必須有一個包含 Main 方法…

SpringBoot整合SSM

1. SSM整合步驟 今天帶大家學習一下基于SpringBoot的SSM整合案例&#xff0c;話不多說&#xff0c;咱們開始&#xff0c;要實現SSM整合&#xff0c;有以下這么幾步 導入依賴創建yml配置文件dao層靜態頁面測試類進行測試 1.1 導入依賴 <?xml version"1.0" enco…

多面體模型-學習筆記2

1&#xff09; 多面體模型被應用于解決程序變換問題&#xff0c;并有效地推動了程 序自動并行化等技術的發展。與傳統的解決程序變換的方法相比&#xff0c;多面體模型 具有許多優勢[5]。多面體模型提供了一種強大的抽象&#xff0c;將每個語句的動態語句執 行實例視作一個多面…

基于django+vue的健身房管理系統-vue

開發語言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.8數據庫&#xff1a;mysql 5.7數據庫工具&#xff1a;Navicat12開發軟件&#xff1a;PyCharm 系統展示 會員信息管理 員工信息管理 會員卡類型管理 健身項目管理 會員卡管理 摘要 健身房管理…

【Linux系統】Linux環境變量:系統配置的隱形指揮官

。# Linux系列 文章目錄 前言一、環境變量的概念二、常見的環境變量三、環境變量特點及其相關指令3.1 環境變量的全局性3.2、環境變量的生命周期 四、環境變量的組織方式五、C語言對環境變量的操作5.1 設置環境變量&#xff1a;setenv5.2 刪除環境變量:unsetenv5.3 遍歷所有環境…

Spring AI中使用ChatMemory實現會話記憶功能

文章目錄 1、需求2、ChatMemory中消息的存儲位置3、實現步驟1、引入依賴2、配置Spring AI3、配置chatmemory4、java層傳遞conversaionId 4、驗證5、完整代碼6、參考文檔 1、需求 我們知道大型語言模型 &#xff08;LLM&#xff09; 是無狀態的&#xff0c;這就意味著他們不會保…