在圖的應用中,最經常使用的就是求解各類條件下的最短路徑問題,這裏Dijkstra迪傑斯特拉算法是求解有權(權值爲非負值)圖的單源最短路徑算法,即只能求出某點到其它點最短距離,並不能得出任意兩點之間的最短距離。ios
經典實現僞代碼以下:c++
1 function Dijkstra(Graph, source):
2
3 create vertex set Q
4
5 for each vertex v in Graph:
6 dist[v] ← INFINITY
7 prev[v] ← UNDEFINED
8 add v to Q
10 dist[source] ← 0
11
12 while Q is not empty:
13 u ← vertex in Q with min dist[u]
14
15 remove u from Q
16
17 for each neighbor v of u: // only v that are still in Q
18 alt ← dist[u] + length(u, v)
19 if alt < dist[v]:
20 dist[v] ← alt
21 prev[v] ← u
22
23 return dist[], prev[]
複製代碼
能夠看到Dijkstra算法核心思想是每次都從距離值最短的頂點開始探索最短路徑,從當前頂點探索更新完本身的鄰接節點後,若是發現有其餘頂點的距離值更小,則跳至那個頂點,繼續探索,直到全部頂點都被處理過。其算法過程可參考下圖: 算法
1 S ← empty sequence
2 u ← target
3 if prev[u] is defined or u = source: // Do something only if the vertex is reachable
4 while u is defined: // Construct the shortest path with a stack S
5 insert u at the beginning of S // Push the vertex onto the stack
6 u ← prev[u] // Traverse from target to source
複製代碼
Dijkstra算法(包括經典實現和優先級隊列實現)完整的C++代碼實現以下:bash
// Dijkstra最短路徑算法
#include <cassert>
#include<vector>
#include<list>
#include<queue>
#include<iostream>
#include<utility>
#include<climits>
#include<algorithm>
using namespace std;
class Vertex {
public:
Vertex(int v, int d = INT_MAX):vertex(v),distance(d){}
Vertex(const Vertex& v) {
this->vertex = v.vertex;
this->distance = v.distance;
}
void setDistance(int d) {
this->distance = d;
}
int vertex;
int distance;
bool operator< (const Vertex &v) const {
return this->distance > v.distance;
}
};
// 無向有權(非負值)圖
class Graph {
public:
Graph(int numVertices) {
this->numVertices = numVertices;
matrix = new int*[numVertices];
for (int i = 0; i < numVertices; ++i) {
matrix[i] = new int[numVertices];
for (int j = 0; j < numVertices; ++j) {
matrix[i][j] = 0;
}
}
}
void addEdge(int v1, int v2, int value = 1) {
matrix[v1][v2] = value;
matrix[v2][v1] = value;
}
void removeEdge(int v1, int v2) {
matrix[v1][v2] = 0;
matrix[v2][v1] = 0;
}
list<int> dijkstraShortestPath(int src, int dst) {
assert(src != dst);
// vector<int> prev = dijkstra(src);
vector<int> prev = dijkstra_priority_queue(src);
list<int> path;
int u = dst;
if (prev[u] != -1) {
while (prev[u] != -1) {
path.push_front(u);
u = prev[u];
}
}
return path;
}
// dijkstra經典實現,算法複雜度O(|V|^2)
vector<int> dijkstra(int source) {
vector<int> prev;
vector<int> dist;
list<int> vlist;
for (int i = 0; i < this->numVertices; ++i) {
dist.push_back(INT_MAX);
prev.push_back(-1);
vlist.push_back(i);
}
dist[source] = 0;
while (!vlist.empty()) {
int u = minVertexDistance(&vlist, &dist);
vlist.remove(u);
vector<int> neighbors = neighbor_vertices(u);
for (auto it = neighbors.begin(); it != neighbors.end(); ++it) {
int alt = dist[u] + distance(u, *it);
if (alt < dist[*it]) {
dist[*it] = alt;
prev[*it] = u;
}
}
}
// 打印距離值
for (int i = 0; i < numVertices; ++i) {
cout << i << " : " << dist[i] << endl;
}
return prev;
}
// dijkstra優先隊列實現
vector<int> dijkstra_priority_queue(int source) {
priority_queue<Vertex> pq;
vector<int> dist;
vector<int> prev;
for (int i = 0; i < this->numVertices; ++i) {
if (i != source) {
dist.push_back(INT_MAX);
} else {
dist.push_back(0);
}
prev.push_back(-1);
}
Vertex vsrc(source, 0);
pq.push(vsrc);
while (!pq.empty()) {
Vertex u(pq.top());
pq.pop();
vector<int> neighbor = neighbor_vertices(u.vertex);
for (auto it = neighbor.begin(); it != neighbor.end(); ++it) {
int alt = dist[u.vertex] + distance(u.vertex, *it);
if (alt < dist[*it]) {
dist[*it] = alt;
prev[*it] = u.vertex;
pq.push(Vertex(*it, alt));
}
}
}
// 打印距離值
for (int i = 0; i < numVertices; ++i) {
cout << i << " : " << dist[i] << endl;
}
return prev;
}
vector<int> neighbor_vertices(int vertex) {
vector<int> neighbor;
for (int i = 0; i < numVertices; ++i) {
if (matrix[vertex][i] > 0) {
neighbor.push_back(i);
}
}
return neighbor;
}
int distance(int v1, int v2) {
return matrix[v1][v2];
}
void print() {
for (int i = 0; i < numVertices; ++i) {
cout << i << " : ";
for (int j = 0; j < numVertices; ++j) {
cout << matrix[i][j] << " ";
}
cout << endl;
}
}
~Graph() {
for (int i = 0; i < numVertices; ++i) {
delete[] matrix[i];
}
delete[] matrix;
}
private:
int **matrix;
int numVertices;
bool isEdge(int v1, int v2) {
return matrix[v1][v2] >= 0 ? true : false ;
}
int minVertexDistance(list<int> *vlist, vector<int> *dist) {
assert(!vlist->empty() && !dist->empty());
auto it = vlist->begin();
int v = *it;
int min = (*dist)[v];
for ( ;it!= vlist->end(); ++it) {
if ((*dist)[*it] < min) {
v = *it;
}
}
return v;
}
};
/*
鄰接矩陣
0 : 0 3 2 0 0
1 : 3 0 0 1 0
2 : 2 0 0 5 8
3 : 0 1 5 0 4
4 : 0 0 8 4 0
距離
0 : 0
1 : 3
2 : 2
3 : 4
4 : 8
最短路徑:path: 1 3 4
*/
void test() {
Graph g(5);
g.addEdge(0, 1, 3);
g.addEdge(0, 2, 2);
g.addEdge(1, 3, 1);
g.addEdge(2, 3, 5);
g.addEdge(2, 4, 8);
g.addEdge(3, 4, 4);
g.print();
// vector<int> prev = g.dijkstra(0);
// vector<int> prev = g.dijkstra_priority_queue(0);
list<int> path = g.dijkstraShortestPath(0, 4);
cout << "path: ";
for (auto it = path.begin(); it != path.end(); ++it) {
cout << *it << " ";
}
}
int main() {
test();
return 0;
}
複製代碼
最後須要注意Dijkstra的適用性,它不適用於包含負權邊的圖。由於Dijkstra算法基於這樣的假設:對於處理過的節點,沒有前往該節點的更短路徑。這種假設僅在沒有負權邊時才成立。所以,不能將Dijkstra's algorithm用於包含負權邊的圖。ui
參考文檔:Dijkstra's algorithmthis