阅读 15

Dijkstra算法

在图的应用中,最常用的就是求解各种条件下的最短路径问题,这里Dijkstra迪杰斯特拉算法是求解有权(权值为非负值)图的单源最短路径算法,即只能求出某点到其它点最短距离,并不能得出任意两点之间的最短距离。

经典实现伪代码如下:

 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++代码实现如下:

// 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用于包含负权边的图。

参考文档:Dijkstra's algorithm

关注下面的标签,发现更多相似文章
评论