最短路徑演算法—Dijkstra(迪傑斯特拉)演算法分析與實現(C/C++)及其他 + leetcode習題實踐
最短路徑求解
最短路徑的常用解法有迪傑克斯特拉演算法Dijkstra Algorithm, 弗洛伊德演算法Floyd-Warshall Algorithm, 和貝爾曼福特演算法Bellman-Ford Algorithm,其中,Floyd演算法是多源最短路徑,即求任意點到任意點到最短路徑,而Dijkstra演算法和Bellman-Ford演算法是單源最短路徑,即單個點到任意點到最短路徑。單源最短路徑(無負權值):
Dijkstra(迪傑斯特拉)演算法是典型的最短路徑路由演算法,用於計算一個節點到其他所有節點的最短路徑。主要特點是以起始點為中心向外層層擴充套件,直到擴充套件到終點為止。
Dijkstra演算法是很有代表性的最短路演算法,在很多專業課程中都作為基本內容有詳細的介紹,如資料結構,圖論,運籌學等等。
其基本思想是,設定頂點集合S並不斷地作貪心選擇來擴充這個集合。一個頂點屬於集合S當且僅當從源到該頂點的最短路徑長度已知。
初始時,S中僅含有源。設u是G的某一個頂點,把從源到u且中間只經過S中頂點的路稱為從源到u的特殊路徑,並用陣列dist記錄當前每個頂點所對應的最短特殊路徑長度。Dijkstra演算法每次從V-S中取出具有最短特殊路長度的頂點u,將u新增到S中,同時對陣列dist作必要的修改。一旦S包含了所有V中頂點,dist就記錄了從源到所有其它頂點之間的最短路徑長度。
程式碼實現
// AllTest.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #include <iostream> #include <string.h> #include <stdio.h> #include <algorithm> #include <stack> using namespace std; #define INT_MAX1 999999 typedef struct MGraph { int n,line; int m[100][100]; }gg,*pG; // g -- adjacent matrix pointer // v0 -- the source node // dist[] -- the distance from the ith node to the source node // prev[] -- the previous node of the ith node void Dijkstra (pG g,int *pre,int *dis,int v0) { bool visited[100]; // 判斷是否已存入該點到S集合中 int n=g->n; for (int i=1;i<=n;i++) { dis[i]=g->m[v0][i]; //初始dis visited[i]=false; // 初始都未用過該點 if (dis[i]==INT_MAX1) //初始pre pre[i]=0; else pre[i]=v0; } visited[v0]=true; //初始起點 dis[v0]=0; // 依次將未放入S集合的結點中,取dis[]最小值的結點,放入結合S中 // 一旦S包含了所有V中頂點,dis就記錄了從源點到所有其他頂點之間的最短路徑長度 // 注意是從第二個節點開始,第一個為源點 for (int i=2;i<=g->n;i++) //1 { int u=v0,min=INT_MAX1; // 找出當前未使用的點j的dis[j]最小值 for (int j=1;j<=g->n;j++) // if (!visited[j]&&dis[j]<min) { u=j; // u儲存當前鄰接點中距離最小的點的號碼 min=dis[j]; } visited[u]=true; dis[u]=min;//其實這個不需要再賦值 //更新dis for (int j=1;j<=g->n;j++) if (!visited[j]&&g->m[u][j]<INT_MAX1&&dis[u]+g->m[u][j]<dis[j]) { pre[j]=u; dis[j]=dis[u]+g->m[u][j]; } } } // 查詢從源點v到終點u的路徑,並輸出 void research(pG g,int *pre,int v0,int u) { int t=pre[u]; stack<int> s; s.push(u); while (t!=v0) { s.push(t); t=pre[t]; } s.push(v0); while (!s.empty()) { int output=s.top(); s.pop(); if (output==v0) cout<<v0; else cout<<"->"<<output; } } int main() { MGraph g; freopen("input.txt","r",stdin); cin>>g.n; cin>>g.line; for (int i=1;i<=g.n;i++) for (int j=1;j<=g.n;j++) g.m[i][j]=INT_MAX1; int dis[100]; generate(dis,dis+100,[](){return INT_MAX1;}); int pre[100]; generate(pre,pre+100,[](){return 1;}); for (int i=1;i<=g.line;i++) { int x,y,weight; cin>>x>>y>>weight; g.m[x][y]=weight; //g.m[y][x]=weight;//無向圖 } for(int i=1; i<=g.n; ++i) { for(int j=1; j<=g.n; ++j) printf("%8d", g.m[i][j]); printf("\n"); } Dijkstra(&g,pre,dis,1); cout<<"源點到最後一個頂點的最短路徑長度: "<< dis[g.n] << endl; // 路徑 cout << "源點到最後一個頂點的路徑為: "; research(&g,pre,1,g.n); return 0; }
資料
5
7
1 2 10
1 4 30
1 5 100
2 3 50
3 5 10
4 3 20
4 5 60
單源最短路徑(有負權值)
任意兩點最短路徑
LeetCode 743. Network Delay Time
Desciption
There are N
network nodes, labelled 1
to N
.
Given times
, a list of travel times as directed edges times[i]
= (u, v, w)
, where u
is the source node, v
is
the target node, and w
is the time it takes for a signal to travel from source to target.
Now, we send a signal from a certain node K
. How long will it take for all nodes to receive the signal?
If it is impossible, return -1
.
Note:
N
will be in the range[1, 100]
.K
will be in the range[1, N]
.- The length of
times
will be in the range[1, 6000]
. - All edges
times[i] = (u, v, w)
will have1 <= u, v <= N
and1 <= w <= 100
.
解題思路:
基於Bellman-Ford演算法的解法,時間複雜度是O(VE),V和E分別是結點和邊的個數。這種演算法是基於DP來求全域性最優解,原理是對圖進行V - 1次鬆弛操作,這裡的V是所有結點的個數(為啥是V-1次呢,因為最短路徑最多隻有V-1條邊,所以只需迴圈V-1次),在重複計算中,使得每個結點的距離被不停的更新,直到獲得最小的距離,這種設計方法融合了暴力搜尋之美,寫法簡潔又不失優雅。之前提到了,Bellman-Ford演算法可以處理負權重的情況,但是不能有負環存在,一般形式的寫法中最後一部分是檢測負環的,如果存在負環則報錯。不能有負環原因是,每轉一圈,權重和都在減小,可以無限轉,那麼最後的最小距離都是負無窮,無意義了。沒有負環的話,V-1次迴圈後各點的最小距離應該已經收斂了,所以在檢測負環時,就再迴圈一次,如果最小距離還能更新的話,就說明存在負環。這道題由於不存在負權重,所以就不檢測了。class Solution {
public://最短路徑中的最大值
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
vector<int> dist(N+1,INT_MAX);
dist[K]=0;
for (int i=1;i<N;++i)
for (auto e:times){
int u=e[0],v=e[1],w=e[2];
if (dist[u]!=INT_MAX&&dist[u]+w<dist[v])//update edge of u vertex
dist[v]=dist[u]+w;
}
int max=0;
for (int i=1;i<=N;++i)
max=(dist[i]>max?dist[i]:max);
return max==INT_MAX?-1:max;
}
};