Dijkstra算法

在圖的應用中,最經常使用的就是求解各類條件下的最短路徑問題,這裏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算法核心思想是每次都從距離值最短的頂點開始探索最短路徑,從當前頂點探索更新完本身的鄰接節點後,若是發現有其餘頂點的距離值更小,則跳至那個頂點,繼續探索,直到全部頂點都被處理過。其算法過程可參考下圖: 算法

image
上面會計算出源結點到其餘結點的最短距離,若是是要得到源節點到目的節點之間的最短路徑,僞代碼以下:

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

相關文章
相關標籤/搜索