最短路徑算法(算法篇)

算法之最短路徑算法

最短路徑算法

概念

  • 考查最短路徑問題,可能會輸入一個賦權圖(也就是邊帶有權的圖),則一條路徑的v1v2…vN的值就是對路徑的邊的權求和,這叫做賦權路徑長,如果是無權路徑長就是單純的路徑上的邊數

  • 在賦權圖,可能會出現負值邊的情況,這樣當我們去找最短路徑時,可能會產生負值圈,畢竟一直走負值邊可以將數值變得更短。

單源最短路徑問題

  • 給定一個賦權圖G=(V,E)和一個特定頂點s作為輸入,找出從s到G中每一個其他頂點的最短賦權路徑。

無權最短路徑

  • 給定一個無權圖G=(V,E)和一個起始頂點s作為輸入,找出從s到G中每一個其他頂點的最短路徑。

廣度優先搜索算法(BFS)

概念

  • 廣度優先搜索算法(BFS)用于在無權圖或者邊權相同的圖中尋找最短路徑。
  • 該方法按層處理頂點,首先從起始點出發,進行發散找到與起始點鄰接的頂點a,…,并將s到這些頂點的路徑距離更新,然后將該點標記成已經訪問的頂點并將該點的前一個頂點記錄下來(被標記的頂點我們后面遇到就認為該點已經不需要再進行處理了),然后再從頂點a,…發散,找到該頂點的鄰接頂點,然后重復操作直到所有頂點都被標記完,就完成了搜索。
  • 具體代碼實現,是用一個隊列,在迭代開始時,隊列中只含有距離為迭代距離currdist的那些頂點,然后執行操作時,將距離currdist+1的頂點的那些頂點添加到隊列中,只要當前距離為currdist頂點處理完,就會處理距離為currdist+1(也就是當前頂點發散的頂點)的頂點添加到隊列中。
  • 在隊列中其實可以將know域也就是標記去掉,因為隊列的形式已經說明執行過了,就不會在執行,因此相當于標記了。

代碼:

//圖的鄰接表的結點信息
struct listnode{int data;bool flag;    //判斷是否訪問過int path;     //存儲上一個頂點int dist;     //距離listnode* next;
};//圖的信息
class graph{
private:listnode *an;   //鄰接表形式存儲圖int vnum;     //圖中結點數
};//s為起點,an數組的鄰接表表示圖
void BFS(int s){queue<int>q;q.push(s);an[s].dist=0;while (!q.empty()){int v=q.front();q.pop();an[v].flag= true;listnode* p=an[v].next;while (p!= nullptr){if(an[p->data].dist==INT_MAX){an[p->data].dist=an[v].dist+1;an[p->data].path=v;q.push(p->data);}p=p->next;}}}

Dijkstra算法

概念

  • 用于求解賦權圖的最短路徑(無負值邊),Dijkstra算法是解決單源最短路徑問題的一般方法,并且該解法是貪心算法。Dijkstra只是BFS的升級版使他能夠求賦權圖的最短路徑,如果求無權圖Dijkstra跟BFS的做法一樣!
  • Dijkstra算法是分階段的,該算法認為每一個階段,都將該階段當作最好的情況處理,類似于BFS算法,但是還是有不同的地方,比起BFS多出了需要進行每個階段出現最好情況就進行更新路徑。
  • 具體做法是,從圖中選取起始點v,然后找出鄰接點,并將當前起始點到鄰接點v3,v4…的距離更新,如果是賦權圖就是dv+Cv,v3(就是頂點v到v3的權),如果是無權就是dv+1,并將v標記為已知。然后選取鄰接點集中的一點再做為起始點,然后重復操作,將當前頂點的前一個頂點記錄。當v到某個頂點的距離在當前階段是最小的(最好情況),那么就進行更新,如果不是就無需更新
  • 具體來說,當我們擴展一個新結點時,我們會考慮它的所有未訪問過的鄰接結點,并計算從起始結點經過當前結點到達鄰接結點的路徑長度。如果這個長度小于已知的最短路徑長度(或者鄰接結點的路徑長度尚未初始化),我們就更新鄰接結點的路徑長度。這樣做的目的是通過不斷更新路徑長度來找到起始結點到所有其他結點的最短路徑。
  • 實現的時候可以使用優先隊列來進行優化算法,只將頂點和其最短路徑值進入隊列中當隊列非空,執行以下操作:u等于隊頂的節點w等于隊頂節點的最短路徑值遍歷u的所有邊,如果能找到節點v最短路徑值小于v的當前值,更新v,將v壓入隊列。結束
  • 沒有用優先隊列優化的Dijkstra算法的時間復雜度為O(N2),如果使用優先隊列,則時間復雜度為O(nlogn),可以不用考慮已知域;

Dijkstra跟BFS區別:

  1. 處理頂點
    • BFS算法中,當一個頂點被擴展時,它的所有未訪問過的鄰居頂點都被添加到隊列中,這樣它們將按照遍歷的順序逐個被訪問。
    • Dijkstra算法中,當一個頂點被擴展時,它的鄰居頂點也被考慮,但是Dijkstra算法會計算擴展的頂點與其鄰居之間的邊的權重,并根據這個權重來更新到達鄰居頂點的最短路徑長度。這個更新過程使得Dijkstra算法能夠處理帶有非負權重的圖。
  2. 選擇下一個頂點
    • BFS算法中,下一個要被擴展的頂點是隊列中的下一個頂點,也就是按照隊列的先進先出(FIFO)原則選擇。
    • Dijkstra算法中,下一個要被擴展的頂點是距離起始點路徑長度最小的頂點,也就是根據已知的最短路徑長度來選擇。這需要使用優先隊列或者最小堆來高效地選擇路徑長度最小的頂點。

代碼:

//圖的鄰接表的結點信息
struct listnode{int data;int path;     //存儲上一個頂點int dist;     //最短距離int weight;   //數組索引頂點跟該頂點的邊的權重listnode* next;
};//圖的信息
class graph{
private:listnode *an;   //鄰接表形式存儲圖int vnum;     //圖中結點數
};//v是起始點
void Dijkstra(int v){an[v].dist=0;queue<int>q;q.push(v);while (!q.empty()){int w=q.front();q.pop();listnode* p=an[w].next;while (p!= nullptr){if(an[w].dist+p->weight<an[p->data].dist){an[p->data].dist=an[w].dist+p->weight;an[p->data].path=w;q.push(p->data);}p=p->next;}}}

題目模板
有向邊單源最短路徑問題

#include <bits/stdc++.h>
using namespace std;const int INF=0x3f3f3f3f;const int N=10;
int n;struct edge {int v, w;
};bool vis[N+1];int dijkstra(int start, const vector<vector<edge>>& graph) {int minroad[n+1];memset(minroad,INF,sizeof minroad);minroad[start] = 0;priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;pq.push({0, start});while (!pq.empty()) {auto [d, u] = pq.top();pq.pop();if(vis[u]) continue;vis[u]=true;for (const auto& edges : graph[u]) {int v = edges.v;int w = edges.w;if (minroad[u] + w < minroad[v]) {minroad[v] = minroad[u] + w;pq.push({minroad[v], v});}}}return minroad[n]!=INF?minroad[n]:-1;
}int main() {int m, start;cin >> n >> m >> start;vector<vector<edge>> graph(n + 1);for (int i = 0; i < m; i++) {int u, v, w;cin >> u >> v >> w;graph[u].push_back({v, w});}cout<<dijkstra(start, graph)<<endl;system("pause");return 0;
}

Floyd算法

概念

  • Floyd(弗洛伊德)算法是基于動態規劃思想的算法,也稱插點法,是全源最短路算法(全源就代表經過一次Floyd算法,每個點到各個點的最短路徑都能算出來)
  • 用于求任意一對頂點間的最短路徑,此時圖中的邊的權值可以出現負數,但不能出現負環
  • 時間復雜度為O(n3),n為點個數

基本思想

  1. 對于從i到j的弧,進行n次試探
  2. 首先考慮i,0,j是否存在,如果存在,則比較i,j和i,0,j的路徑長度,去最短者進行更新i,j的最短路徑
  3. 然后再添加頂點1,依次類推。

具體過程

  1. 當一個圖里有n個城市,求全源最短路徑問題
  2. 定義城市k為從當前圖拿出來,并重新插入圖中的城市城市i城市j分別為當前源城市目的城市dist[i\][j]表示城市i到城市j的最短路徑
  3. 假設當前圖中沒有城市k,我們將城市k重新插入到圖中
  4. 我們需要判斷城市i到城市j的最短路徑是否要更新,則比較路徑經過城市k和原來的路徑長度進行比較,如果經過城市k的路徑長度更短,則更新dist[i][j],因此就為dist[i][j]=min(dist[i][k]+dist[k][j],dist[i][j])
  5. 因此對這個圖執行n次上述操作(也就是插點法),得出的dist二維數組就為全源的最短路徑。

代碼模板

//dist[n][n]用來記錄圖中各點到各點的最短路徑
void Floyd(int **dist){for(int k=0;k<n;++k){for(int i=0;i<n;++i){for(int j=0;j<n;++j){if(dist[i][k]+dist[k][j]<dist[i][j]){dist[i][j]=dist[i][k]+dist[k][j];}}}}
}

例題部分代碼

具體可看力扣1334. 閾值距離內鄰居最少的城市,只包含求解全源最短路徑代碼

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;void Floyd(int n, vector<vector<int>>& edges){const int INF=0x3f3f3f3f;int dist[n][n];memset(dist,INF, sizeof(dist));for(int i=0;i<n;++i){dist[i][i]=0;}for(auto edge:edges){dist[edge[0]][edge[1]]=edge[2];dist[edge[1]][edge[0]]=edge[2];}//Floyd算法計算全源最短路代碼for(int k=0;k<n;++k){for(int i=0;i<n;++i){for(int j=0;j<n;++j){if(dist[i][k]+dist[k][j]<dist[i][j]){dist[i][j]=dist[i][k]+dist[k][j];}}}}for(int i=0;i<n;++i){cout<<"第"<<i<<"城市到其他城市最短路徑:";for(int j=0;j<n;++j)cout<<"("<<i<<","<<j<<","<<dist[i][j]<<")"<<" ";cout<<endl;}
}int main() {vector<vector<int>>edges{{0,1,2},{0,4,8},{1,2,3},{1,4,2},{2,3,1},{3,4,1}};Floyd(5,edges);system("pause");return 0;
}

尾言

完整版筆記也就是數據結構與算法專欄完整版可到我的博客進行查看,或者在github庫中自取(包含源代碼)

  • 博客1: codebooks.xyz
  • 博客2:moonfordream.github.io
  • github項目地址:Data-Structure-and-Algorithms

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

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

相關文章

mac安裝配置cmake

本機是2015 macbook pro mid&#xff0c;已經有點老了&#xff0c;用homebrew下cmake老出問題 其實cmake官網安裝也不麻煩 一、官網下載對應安裝包 Download CMake 和所有dmg文件一樣安裝 二、改成命令行使用 一般來說 tutorial 給的都是命令行build 命令行的設置如下&am…

手機下載APP (uniapp/vue)

一、uniapp <template><view class"content"><view class"appName">{{ formData.appName }}</view><view class"appInfo">{{ formData.appInfo }}</view><image class"logo" :src"formDa…

批量修改Git歷史commit信息中的username

之前很長一段時間GitHub上的提交都在使用工作賬戶, 導致私人倉庫中的提交者比較混亂. 在StackOver里面找到了一個bash腳本可以批量修改username, 在這里記錄一下. 修改的步驟一共兩步: 執行修改腳本將本地修改同步到Git服務器 首先我們來看腳本: #!/bin/shgit filter-branch…

SFUZZ模糊測試平臺全新升級,從標準到實踐助力車企安全出海

開源網安模糊測試平臺SFuzz全新升級&#xff0c;參照各國相關標準要求進行針對性建設&#xff0c;可為智能網聯汽車信息安全測試提供更為強大的工具支持。SFuzz向被測系統輸入大量隨機數據&#xff0c;模擬各種異常情況&#xff0c;可以發現被測系統內潛在的缺陷和漏洞&#xf…

Spring中如何操作Redis

Spring畢竟是Java中的一個主流框架&#xff0c;如何在這個框架中使用Redis呢&#xff1f; 創建項目并引入相關依賴 然后進行創建。 至此就將Redis的相關依賴引入進來了。 編寫Redis配置 將application.properties修改成application.yml 然后編寫如下配置&#xff1a; spr…

usbserver工程師手記(二)設置定時任務

概述 部分銀行ukey 長時間不使用后會導致休眠&#xff0c;出現雖然有連接&#xff0c;但是讀不到證書&#xff0c;可以用定時重置端口的辦法&#xff0c;調用接口 http://ip/usb_server/reset_port,參數為 {"port":"B5-1-2","vid_pid":"09…

Golang | Leetcode Golang題解之第228題匯總區間

題目&#xff1a; 題解&#xff1a; func summaryRanges(nums []int) (ans []string) {for i, n : 0, len(nums); i < n; {left : ifor i; i < n && nums[i-1]1 nums[i]; i {}s : strconv.Itoa(nums[left])if left < i-1 {s "->" strconv.It…

多個標簽頁中復用同一 QTableView

在 PyQt 中實現在多個標簽頁中復用同一個 QTableView 實例&#xff0c;復用同一個 QTableView 實例可以減少內存和資源的使用。每個 QTableView 實例都會消耗一定的內存和處理資源&#xff0c;如果每個標簽頁都創建一個新的實例&#xff0c;會增加系統的負擔。通過復用實例&…

每天一個數據分析題(四百二十一)- 一元線性回歸模型

關于一元線性回歸的求解過程說法正確的是&#xff1f; A.一元線性回歸只需要求解出兩個參數系數即可 B.對于新來的樣例&#xff0c;建立好的一元線性回歸模型可以做出準確的預測 C.一元線性回歸模型的基本形式是YAxe&#xff0c;其中A為系數&#xff0c;e為隨機誤差 D.一元線性…

日常學習-20240710

1、一次一千萬條數據插入和刪除案例&#xff1a; 第一次&#xff1a;插入--批量插入&#xff0c;每次插入5000條數據&#xff0c;總耗時28min,數據無異常 刪除--通過sql語句一次性刪除&#xff0c;總耗時1h52min;一次刪除的數據過多導致mysql的備份恢復文件極其龐大&#xff0…

CentOS7 安裝 git 命令

通過yum源install下載的git版本比較低&#xff0c;不推薦此方式安裝。 官網下載最新版git源碼&#xff1a;Git 1. 解壓安裝包 tar -xzvf git-2.45.2.tar.gz 2. 安裝相關依賴 yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel gcc perl-ExtUtils…

uniapp使用高德地圖(公眾號+h5)

選擇微信小程序的話后果就是你的地圖出不來&#xff0c;出來了就報key異常 下面直接放配置和代碼&#xff1a; 打包后的高德uni-app,uniCloud,serverless,高德地圖,申請高德地圖Key,配置使用高德地圖,參數說明,高德開放平臺用戶名,百度地圖,申請百度地圖Key,配置使用百度地圖,…

線性代數|機器學習-P22逐步最小化一個函數

文章目錄 1. 概述2. 泰勒公式3. 雅可比矩陣4. 經典牛頓法4.1 經典牛頓法理論4.2 牛頓迭代法解求方程根4.3 牛頓迭代法解求方程根 Python 5. 梯度下降和經典牛頓法5.1 線搜索方法5.2 經典牛頓法 6. 凸優化問題6.1 約束問題6.1 凸集組合 Mit麻省理工教授視頻如下&#xff1a;逐步…

bert訓練的一些技巧(rand() < self.skipgram_prb)

rand() < self.skip_gram_prb) 是一個條件表達式&#xff0c;用來判斷是否進行skip-gram掩碼操作。這種掩碼操作通常用于自然語言處理中的數據增強&#xff0c;通過概率決定是否應用skip-gram掩碼。下面是對這個表達式的詳細解釋&#xff1a; 解釋 rand(): rand() 是一個隨…

uniapp 初始學習1

uni-app代碼基本包括js,vue,css.在app端支持原生渲染nvue&#xff0c;可編譯的kotlin和swift 掌握js就可以進行不同應用的開發 頁面文件遵循 Vue 單文件組件 (SFC) 規范&#xff0c;即每個頁面是一個.vue文件 .vue文件是一個自定義的文件類型&#xff0c;用類HTML語法描述一…

SpringBoot使用RedisTemplate、StringRedisTemplate操作Redis

前言 RedisTemplate 是 Spring Boot 訪問 Redis 的核心組件&#xff0c;底層通過 RedisConnectionFactory 對多種 Redis 驅動進行集成&#xff0c;上層通過 XXXOperations 提供豐富的 API &#xff0c;并結合 Spring4 基于泛型的 bean 注入&#xff0c;極大的提供了便利&#x…

深度學習和NLP中的注意力和記憶

深度學習和NLP中的注意力和記憶 文章目錄 一、說明二、注意力解決了什么問題&#xff1f;#三、關注的代價#四、機器翻譯之外的關注#五、注意力&#xff08;模糊&#xff09;記憶&#xff1f;# 一、說明 深度學習的最新趨勢是注意力機制。在一次采訪中&#xff0c;現任 OpenAI 研…

使用 python 構建企業級高可用海量爬蟲調度系統

一、引言 在大數據時代&#xff0c;信息的獲取與分析成為了企業決策的重要依據。對于營銷行業而言&#xff0c;實時抓取和分析競爭對手動態、市場趨勢以及用戶反饋等數據&#xff0c;是制定有效策略的關鍵。然而&#xff0c;構建一個高可用的、能夠處理海量數據的爬蟲調度系統…

K8S中部署 Nacos 集群

1. 準備 GitK8Skubectlhelm 咱也沒想到 K8S 部署系列能搞這么多次&#xff0c;我一個開發天天干運維的活&#xff0c;前端后端運維測試工程師實至名歸。 2. 方案選擇 https://github.com/nacos-group/nacos-k8s 我替你們看了一下&#xff0c;有好幾種方式能部署&#xff…

華為機考真題 -- 求字符串中所有整數

題目描述&#xff1a; 輸入字符串s&#xff0c;輸出s中包含所有整數的最小和。 說明&#xff1a;字符串s&#xff0c;只包含 a-z A-Z &#xff1b; 合法的整數包括&#xff1a; 1&#xff09; 正整數 一個或者多個0-9組成&#xff0c;如 0 2 3 002 102 2&#xff09;負整數…