最短路徑——Dijkstra演算法和Floyd演算法
一、最短路徑概念
圖中一個頂點到另一個頂點可能存在多條路徑,每條路徑所經過邊的數量可能不同,每條路徑所經過邊的權值也可能不同,我們把花費最小的路徑成為兩個頂點的最短路徑。
最短路徑相關的兩種常用演算法:迪克斯特拉(Dijstra)演算法和弗洛伊德(Floyd)演算法。Dijstra演算法用於快速求得圖中一個頂點到其他所有頂點的最短距離和路徑,Floyd演算法用於求圖中每對頂點的最短路徑。
二、應用
最短路徑問題已經應用在了諸多領域,如地圖導航,公路建設,路由器定址等等。
三、Dijstra演算法和Floyd演算法的具體實現
(1)Dijstra演算法
Dijstra演算法的基本思想是:將圖中的頂點集合分成兩組,第1組為已求出最短路徑的頂點集合S,第2組為其餘未確定最短路徑的頂點集合U。然後從一個已知的頂點k開始,尋找離k最近的頂點imin,然後把頂點imin加入到第1組頂點集合S中,如果以頂點imin作為中間點到其他頂點的距離更短,則設定imin最為路徑的中間點,並更新k到其他頂點的最短距離,重複尋找最近頂點imin直至所有頂點都加入到集合S中。
其執行步驟如下:
①將圖中的頂點集合分成兩組,第1組為已求出最短路徑的頂點集合S,第2組為其餘未確定最短路徑的頂點集合U。
②將已知的起始點k加入S中,並初始化k到其他頂點的最短距離為有向圖相關邊的權值(若不存在邊,設定距離為無窮大)。
③重複步驟④,直至所有頂點都加入到集合S中。
④尋找離k最近的頂點imin,然後把頂點imin加入到第1組頂點集合S中,如果以頂點imin作為中間點到其他頂點的距離更短,則設定imin最為路徑的中間點,並更新k到其他頂點的最短距離。
C++程式碼實現:
圖的鄰接矩陣宣告:
#define N 100
typedef char ElemType;
/*
圖的鄰接矩陣宣告
*/
typedef struct _MGraph
{
int edges[N][N]; //邊集合
int n; //頂點數
}MGraph;
Dijstra演算法:
/*
迪克斯特拉演算法
g儲存有向圖的邊
k代表出發的頂點
path[i]儲存第i個頂點的上一個頂點
dis[i]儲存從k出發到頂點i的最短距離
*/
void Dijkstra(MGraph &g, int k, int path[], int dis[])
{
int* visited = new int[g.n](); //儲存頂點是否被訪問過,初始化為0
for (int i = 0; i < g.n; i++)
{
dis[i] = g.edges[k][i]; //初始化最短距離陣列
path[i] = k; //初始化路徑陣列
}
visited[k] = 1;
dis[k] = 0;
for (int cnt = 1; cnt < g.n; cnt++) //迴圈n-1次
{
int imin = -1; //儲存最短邊的下標
for (int i = 0; i < g.n; i++) //尋找沒訪問過的最短邊
{
if (!visited[i] && (imin == -1 || dis[i] < dis[imin]))
imin = i;
}
visited[imin] = 1;
for (int i = 0; i < g.n; i++) //如果新的頂點到其他頂點的距離更短,更新最短距離和路徑
{
if (!visited[i] && dis[imin] + g.edges[imin][i] < dis[i])
{
dis[i] = dis[imin] + g.edges[imin][i];
path[i] = imin;
}
}
}
delete[] visited; //記得釋放記憶體
}
根據Dijstra演算法生成的path陣列輸出路徑:
/*
輸出從開始頂點到頂點k的最短路徑
*/
void DisplayPath(int k, int path[])
{
stack<int> s;
while (path[k] != k)
{
s.push(k);
k = path[k];
}
s.push(k);
int cnt = 0;
while (!s.empty())
{
if (cnt++ > 0) cout << "->";
cout << s.top();
s.pop();
}
cout << endl;
}
Dijstra演算法包含了兩重for迴圈,其時間複雜度為O(n²)。
(2)Floyd演算法
Floyd演算法的基本思想是:用一個二維陣列dis來儲存每對頂點之間的最短路徑長度,即dis[i][j]表示從頂點i到頂點j的最短路徑長度,dis陣列初始化為圖的鄰接矩陣陣列dis[i][j] = g.edges[i][j]。從頂點k=0開始,將k作為中間節點,如果頂點i以k最為中間節點到達頂點j的距離更短,則設定k為中間節點,並更新i到j的最短路徑。將k的值加1,重複選取中間節點直到所有頂點都被假設為中間節點為止。
其執行步驟如下:
①用一個二維陣列dis來儲存每對頂點之間的最短路徑長度,即dis[i][j]表示從頂點i到頂點j的最短路徑長度,dis陣列初始化為圖的鄰接矩陣陣列dis[i][j] = g.edges[i][j]。
②從頂點k=0開始,將k假設為中間節點,重複步驟③直至所有頂點都被假設為中間節點為止。
③如果頂點i以k最為中間節點到達頂點j的距離更短,則設定k為中間節點,並更新i到j的最短路徑。
C++程式碼實現:
Floyd演算法:
/*
弗洛伊德演算法
g儲存有向圖的邊
dis[i][j]儲存頂點i到頂點j的最短距離長度
path[i][j]儲存頂點j的上一個頂點
*/
void Floyd(MGraph& g, int dis[][N], int path[][N])
{
for (int i = 0; i < g.n; i++)
{
for (int j = 0; j < g.n; j++)
{
dis[i][j] = (i == j ? 0 : g.edges[i][j]); //初始化距離陣列
path[i][j] = i; //初始化路徑陣列
}
}
for (int k = 0; k < g.n; k++)
{
for (int i = 0; i < g.n; i++)
{
for (int j = 0; j < g.n; j++)
{
if (dis[i][k] + dis[k][j] < dis[i][j]) //如果以K為中間點距離更短,更新距離陣列和路徑陣列
{
dis[i][j] = dis[i][k] + dis[k][j];
path[i][j] = path[k][j];
}
}
}
}
}
根據Floyd生成的path陣列輸出最短路徑:
/*
輸出每對頂點的最短路徑
*/
void DisplayPath(int n, int path[][N], int dis[][N])
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cout << i << "-" << j << "最短路徑長度:" << dis[i][j] << " 最短路徑:";
DisplayPath(j, path[i]);
}
}
}
Floyd演算法包含三重for迴圈,其時間複雜度為O(n³),相當於以每個頂點作為源點呼叫Dijstra演算法的時間複雜度,但Floyd演算法程式碼實現簡單。
四、測試
問題:
輸入一個有向圖,求頂點0到其他頂點的最短路徑長度和最短路徑,以及每對頂點的最短路徑長度和最短路徑。
樣例輸入:
4 8
0 1 5
0 3 7
1 2 4
1 3 2
2 0 3
2 1 3
2 3 2
3 2 1
樣例輸出:
0-0最短路徑長度:0 最短路徑:0
0-1最短路徑長度:5 最短路徑:0->1
0-2最短路徑長度:8 最短路徑:0->3->2
0-3最短路徑長度:7 最短路徑:0->3
0-0最短路徑長度:0 最短路徑:0
0-1最短路徑長度:5 最短路徑:0->1
0-2最短路徑長度:8 最短路徑:0->3->2
0-3最短路徑長度:7 最短路徑:0->3
1-0最短路徑長度:6 最短路徑:1->3->2->0
1-1最短路徑長度:0 最短路徑:1
1-2最短路徑長度:3 最短路徑:1->3->2
1-3最短路徑長度:2 最短路徑:1->3
2-0最短路徑長度:3 最短路徑:2->0
2-1最短路徑長度:3 最短路徑:2->1
2-2最短路徑長度:0 最短路徑:2
2-3最短路徑長度:2 最短路徑:2->3
3-0最短路徑長度:4 最短路徑:3->2->0
3-1最短路徑長度:4 最短路徑:3->2->1
3-2最短路徑長度:1 最短路徑:3->2
3-3最短路徑長度:0 最短路徑:3
#include <iostream>
#include <stack>
using namespace std;
#define N 100
typedef char ElemType;
/*
圖的鄰接矩陣宣告
*/
typedef struct _MGraph
{
int edges[N][N]; //邊集合
int n; //頂點數
}MGraph;
/*
迪克斯特拉演算法
g儲存有向圖的邊
k代表出發的頂點
path[i]儲存第i個頂點的上一個頂點
dis[i]儲存從k出發到頂點i的最短距離
*/
void Dijkstra(MGraph &g, int k, int path[], int dis[])
{
int* visited = new int[g.n](); //儲存頂點是否被訪問過,初始化為0
for (int i = 0; i < g.n; i++)
{
dis[i] = g.edges[k][i]; //初始化最短距離陣列
path[i] = k; //初始化路徑陣列
}
visited[k] = 1;
dis[k] = 0;
for (int cnt = 1; cnt < g.n; cnt++) //迴圈n-1次
{
int imin = -1; //儲存最短邊的下標
for (int i = 0; i < g.n; i++) //尋找沒訪問過的最短邊
{
if (!visited[i] && (imin == -1 || dis[i] < dis[imin]))
imin = i;
}
visited[imin] = 1;
for (int i = 0; i < g.n; i++) //如果新的頂點到其他頂點的距離更短,更新最短距離和路徑
{
if (!visited[i] && dis[imin] + g.edges[imin][i] < dis[i])
{
dis[i] = dis[imin] + g.edges[imin][i];
path[i] = imin;
}
}
}
delete[] visited; //記得釋放記憶體
}
/*
輸出從開始頂點到頂點k的最短路徑
*/
void DisplayPath(int k, int path[])
{
stack<int> s;
while (path[k] != k)
{
s.push(k);
k = path[k];
}
s.push(k);
int cnt = 0;
while (!s.empty())
{
if(cnt++ > 0) cout << "->";
cout << s.top();
s.pop();
}
cout << endl;
}
/*
弗洛伊德演算法
g儲存有向圖的邊
dis[i][j]儲存頂點i到頂點j的最短距離長度
path[i][j]儲存頂點j的上一個頂點
*/
void Floyd(MGraph& g, int dis[][N], int path[][N])
{
for (int i = 0; i < g.n; i++)
{
for (int j = 0; j < g.n; j++)
{
dis[i][j] = (i == j ? 0 : g.edges[i][j]); //初始化距離陣列
path[i][j] = i; //初始化路徑陣列
}
}
for (int k = 0; k < g.n; k++)
{
for (int i = 0; i < g.n; i++)
{
for (int j = 0; j < g.n; j++)
{
if (dis[i][k] + dis[k][j] < dis[i][j]) //如果以K為中間點距離更短,更新距離陣列和路徑陣列
{
dis[i][j] = dis[i][k] + dis[k][j];
path[i][j] = path[k][j];
}
}
}
}
}
/*
輸出每對頂點的最短路徑
*/
void DisplayPath(int n, int path[][N], int dis[][N])
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cout << i << "-" << j << "最短路徑長度:" << dis[i][j] << " 最短路徑:";
DisplayPath(j, path[i]);
}
}
}
int main()
{
MGraph g;
while (cin >> g.n)
{
for (int i = 0; i < g.n; i++)
for (int j = 0; j < g.n; j++)
g.edges[i][j] = INT16_MAX;
int m, u, v, w;
cin >> m;
while (m-- > 0)
{
cin >> u >> v >> w;
g.edges[u][v] = w;
}
//Dijkstra
{
int* path = new int[g.n];
int* dis = new int[g.n];
Dijkstra(g, 0, path, dis);
for (int i = 0; i < g.n; i++)
{
cout << 0 << "-" << i << "最短路徑長度:" << dis[i] << " 最短路徑:";
DisplayPath(i, path);
}
delete[] path, dis;
}
//Floyd
{
int path[N][N], dis[N][N];
Floyd(g, dis, path);
DisplayPath(g.n, path, dis);
}
}
return 0;
}
參考文獻
[1] 李春葆.資料結構教程.清華大學出版社,2013.
相關文章
- 最短路徑(dijkstra 與 Floyd)
- 最短路徑--dijkstra演算法、弗洛伊德(Floyd)演算法(帶路徑輸出)演算法
- 最短路徑(Floyd演算法)演算法
- 圖的最短路徑(Dijkstra | Floyd)
- 最短路徑之Floyd演算法演算法
- [MATLAB]最短路徑Floyd演算法Matlab演算法
- 最短路徑之Dijkstra演算法演算法
- Floyd演算法(計算最短路徑)演算法
- 多源最短路徑演算法:Floyd演算法演算法
- 最短路徑問題 (dijkstra演算法)演算法
- 單源最短路徑-Dijkstra演算法演算法
- 求最短路徑——DFS+Floyd演算法演算法
- 0016:單源最短路徑(dijkstra演算法)演算法
- Floyd最短路演算法演算法
- 最短路徑——floyd演算法程式碼(c語言)演算法C語言
- 最短路徑—Dijkstra(迪傑斯特拉)演算法演算法
- 最短路徑——dijkstra演算法程式碼(c語言)演算法C語言
- 最短路dijkstra演算法演算法
- 最短路 - Dijkstra 演算法演算法
- 多源最短路徑,一文搞懂Floyd演算法演算法
- 最短路演算法之:floyd 演算法演算法
- 最短路-SPFA演算法&Floyd演算法演算法
- python實現Dijkstra演算法之 最短路徑問題Python演算法
- Dijkstra演算法和Floyd演算法超詳解以及區別演算法
- 路徑規劃演算法 - 求解最短路徑 - Dijkstra(迪傑斯特拉)演算法演算法
- 最短路演算法之:Dijkstra 演算法演算法
- [最短路徑問題]Dijkstra演算法(含還原具體路徑)演算法
- 一篇文章講透Dijkstra最短路徑演算法演算法
- dijkstra最短路演算法模板(雙源)演算法
- Python 圖_系列之縱橫對比 Bellman-Ford 和 Dijkstra 最短路徑演算法Python演算法
- 【最短路徑Floyd演算法詳解推導過程】看完這篇,你還能不懂Floyd演算法?還不會?演算法
- 最短路徑演算法演算法
- 10行實現最短路演算法——Dijkstra演算法
- Djikstra最短路徑演算法演算法
- 最短路徑(Dijskra演算法)JS演算法
- 最短路-樸素版Dijkstra演算法&堆優化版的Dijkstra演算法優化
- 最短路徑演算法總結演算法
- 一個人的旅行 (dijkstra演算法求最短路)演算法