zoukankan      html  css  js  c++  java
  • Dijkstra算法

    ---------------------siwuxie095

       

       

       

       

       

       

       

       

    Dijkstra 算法

       

       

    这里介绍 Dijkstra 算法,它是一个应用最为广泛的、名气也是

    最大的单源最短路径算法

       

       

    Dijkstra 算法有一定的局限性:它所处理的图中不能有负权边

       

    「前提:图中不能有负权边」

       

    换句话说,如果一张图中,但凡有一条边的权值是负值,那么

    使用 Dijkstra 算法就可能得到错误的结果

       

       

    不过,在实际生活中所解决的问题,大部分的图是不存在负权

    边的

       

    如:有一个路线图,那么从一点到另外一点的距离肯定是一个

    正数

       

    所以,虽然 Dijkstra 算法有局限性,但是并不影响在实际问题

    的解决中非常普遍的来使用它

       

       

       

       

       

    看如下实例:

       

    1)初始

       

       

       

    左边是一张连通带权有向图,右边是起始顶点 0 到各个顶点的

    当前最短距离的列表,起始顶点 0 到自身的距离是 0

       

       

       

       

    2)将顶点 0 进行标识,并作为当前顶点

       

       

       

    对当前顶点 0 的所有相邻顶点依次进行松弛操作,同时更新列表

       

       

    从列表的未标识顶点中找到当前最短距离最小的顶点,即 顶点 2

    就可以说,起始顶点 0 到顶点 2 的最短路径即 0 -> 2

       

    因为:图中没有负权边,即便存在从顶点 1 到顶点 2 的边,也不

    可能通过松弛操作使得从起始顶点 0 到顶点 2 的距离更小

       

       

    图中没有负权边保证了:对当前顶点的所有相邻顶点依次进行松

    弛操作后,只要能从列表的未标识顶点中找到当前最短距离最小

    的顶点,就能确定起始顶点到该顶点的最短路径

       

       

       

       

    3)将顶点 2 进行标识,并作为当前顶点

       

       

       

       

       

    4)对当前顶点 2 的相邻顶点 1 进行松弛操作,同时更新列表

       

       

       

       

       

    5)对当前顶点 2 的相邻顶点 4 进行松弛操作,同时更新列表

       

       

       

       

       

    6)对当前顶点 2 的相邻顶点 3 进行松弛操作,同时更新列表

       

       

       

    从列表的未标识顶点中找到当前最短距离最小的顶点,即 顶点 1

    就可以说,起始顶点 0 到顶点 1 的最短路径即 0 -> 2 -> 1

       

       

       

       

    7)将顶点 1 进行标识,并作为当前顶点

       

       

       

       

       

    8)对当前顶点 1 的相邻顶点 4 进行松弛操作,同时更新列表

       

       

       

    从列表的未标识顶点中找到当前最短距离最小的顶点,即 顶点 4

    就可以说,起始顶点 0 到顶点 4 的最短路径即 0 -> 2 -> 1 -> 4

       

       

       

       

    9)将顶点 4 进行标识,并作为当前顶点

       

       

       

    当前顶点 4 没有相邻顶点,不必进行松弛操作

       

    从列表的未标识顶点中找到当前最短距离最小的顶点,即 顶点 3

    就可以说,起始顶点 0 到顶点 3 的最短路径即 0 -> 2 -> 3

       

       

       

       

    10)将顶点 3 进行标识,并作为当前顶点

       

       

       

    对当前顶点 3 的相邻顶点 4 进行松弛操作,发现不能通过

    松弛操作使得从起始顶点 0 到顶点 4 的路径更短,所以保

    持原有最短路径不变

       

       

    至此,列表中不存在未标识顶点,Dijkstra 算法结束,找

    到了一棵以顶点 0 为根的最短路径树

       

       

       

       

       

    Dijkstra 算法的过程总结

       

    第一步:从起始顶点开始

       

    第二步:对当前顶点进行标识

       

    第三步:对当前顶点的所有相邻顶点依次进行松弛操作

       

    第四步:更新列表

       

    第五步:从列表的未标识顶点中找到当前最短距离最小

           的顶点,作为新的当前顶点

       

    第六步:重复第二步至第五步,直到列表中不存在未标

                 识顶点

       

       

       

       

       

    其实 Dijkstra 算法主要做两件事情

       

    1)从列表中找最值

       

    2)更新列表

       

       

    显然,借助最小索引堆作为辅助数据结构,就可以非常

    容易地实现这两件事情

       

    最后,Dijkstra 算法的时间复杂度:O(E*logV)

       

       

       

       

       

    程序:

       

    Edge.h:

       

    #ifndef EDGE_H

    #define EDGE_H

       

    #include <iostream>

    #include <cassert>

    using namespace std;

       

       

    //边信息:两个顶点和权值

    template<typename Weight>

    class Edge

    {

       

    private:

       

    int a, b; //边的两个顶点ab(如果是有向图,就默认从顶点a指向顶点b

    Weight weight; //边上的权值

       

    public:

       

    Edge(int a, int b, Weight weight)

    {

    this->a = a;

    this->b = b;

    this->weight = weight;

    }

       

       

    //默认构造函数

    Edge(){}

       

       

    ~Edge(){}

       

       

    int v(){ return a; }

       

       

    int w(){ return b; }

       

       

    Weight wt() { return weight; }

       

       

    //知道边的一个顶点x,返回另一个顶点

    int other(int x)

    {

    assert(x == a || x == b);

    return x == a ? b : a;

    }

       

       

    //友元函数重载

    friend ostream &operator<<(ostream &os, const Edge &e)

    {

    os << e.a << "-" << e.b << ": " << e.weight;

    return os;

    }

       

       

    bool operator<(Edge<Weight> &e)

    {

    return weight < e.wt();

    }

       

       

    bool operator<=(Edge<Weight> &e)

    {

    return weight <= e.wt();

    }

       

       

    bool operator>(Edge<Weight> &e)

    {

    return weight > e.wt();

    }

       

       

    bool operator>=(Edge<Weight> &e)

    {

    return weight >= e.wt();

    }

       

       

    bool operator==(Edge<Weight> &e)

    {

    return weight == e.wt();

    }

    };

       

       

    #endif

       

       

       

    SparseGraph.h:

       

    #ifndef SPARSEGRAPH_H

    #define SPARSEGRAPH_H

       

    #include "Edge.h"

    #include <iostream>

    #include <vector>

    #include <cassert>

    using namespace std;

       

       

       

    // 稀疏图 - 邻接表

    template<typename Weight>

    class SparseGraph

    {

       

    private:

       

    int n, m; //n m 分别表示顶点数和边数

    bool directed; //directed表示是有向图还是无向图

    vector<vector<Edge<Weight> *>> g; //g[i]里存储的就是和顶点i相邻的所有边指针

       

    public:

       

    SparseGraph(int n, bool directed)

    {

    this->n = n;

    this->m = 0;

    this->directed = directed;

    //g[i]初始化为空的vector

    for (int i = 0; i < n; i++)

    {

    g.push_back(vector<Edge<Weight> *>());

    }

    }

       

       

    ~SparseGraph()

    {

       

    for (int i = 0; i < n; i++)

    {

    for (int j = 0; j < g[i].size(); j++)

    {

    delete g[i][j];

    }

    }

    }

       

       

    int V(){ return n; }

    int E(){ return m; }

       

       

    void addEdge(int v, int w, Weight weight)

    {

    assert(v >= 0 && v < n);

    assert(w >= 0 && w < n);

       

    g[v].push_back(new Edge<Weight>(v, w, weight));

    //1)顶点v不等于顶点w,即不是自环边

    //2)且不是有向图,即是无向图

    if (v != w && !directed)

    {

    g[w].push_back(new Edge<Weight>(w, v, weight));

    }

       

    m++;

    }

       

       

    //hasEdge()判断顶点v和顶点w之间是否有边

    //hasEdge()的时间复杂度:O(n)

    bool hasEdge(int v, int w)

    {

    assert(v >= 0 && v < n);

    assert(w >= 0 && w < n);

       

    for (int i = 0; i < g[v].size(); i++)

    {

    if (g[v][i]->other(v) == w)

    {

    return true;

    }

    }

       

    return false;

    }

       

       

    void show()

    {

       

    for (int i = 0; i < n; i++)

    {

    cout << "vertex " << i << ": ";

    for (int j = 0; j < g[i].size(); j++)

    {

    cout << "{to:" << g[i][j]->w() << ",wt:" << g[i][j]->wt() << "} ";

    }

    cout << endl;

    }

    }

       

       

       

    //邻边迭代器(相邻,即 adjacent

    //

    //使用迭代器可以隐藏迭代的过程,按照一定的

    //顺序访问一个容器中的所有元素

    class adjIterator

    {

    private:

       

    SparseGraph &G; //图的引用,即要迭代的图

    int v; //顶点v

    int index; //相邻顶点的索引

       

    public:

       

    adjIterator(SparseGraph &graph, int v) : G(graph)

    {

    this->v = v;

    this->index = 0;

    }

       

       

    //要迭代的第一个元素

    Edge<Weight> *begin()

    {

    //因为有可能多次调用begin()

    //所以显式的将index设置为0

    index = 0;

    //如果g[v]size()不为0

    if (G.g[v].size())

    {

    return G.g[v][index];

    }

       

    return NULL;

    }

       

       

    //要迭代的下一个元素

    Edge<Weight> *next()

    {

    index++;

    if (index < G.g[v].size())

    {

    return G.g[v][index];

    }

       

    return NULL;

    }

       

       

    //判断迭代是否终止

    bool end()

    {

    return index >= G.g[v].size();

    }

    };

    };

       

       

    #endif

       

       

       

    DenseGraph.h:

       

    #ifndef DENSEGRAPH_H

    #define DENSEGRAPH_H

       

    #include "Edge.h"

    #include <iostream>

    #include <vector>

    #include <cassert>

    using namespace std;

       

       

       

    // 稠密图 - 邻接矩阵

    template<typename Weight>

    class DenseGraph

    {

       

    private:

       

    int n, m; //n m 分别表示顶点数和边数

    bool directed; //directed表示是有向图还是无向图

    vector<vector<Edge<Weight> *>> g; //二维矩阵,存储边指针

       

    public:

       

    DenseGraph(int n, bool directed)

    {

    this->n = n;

    this->m = 0;

    this->directed = directed;

    //二维矩阵:nn列,全部初始化为NULL

    for (int i = 0; i < n; i++)

    {

    g.push_back(vector<Edge<Weight> *>(n, NULL));

    }

    }

       

       

    ~DenseGraph()

    {

    for (int i = 0; i < n; i++)

    {

    for (int j = 0; j < n; j++)

    {

    if (g[i][j] != NULL)

    {

    delete g[i][j];

    }

    }

    }

    }

       

       

    int V(){ return n; }

    int E(){ return m; }

       

       

    //在顶点v和顶点w之间建立一条边

    void addEdge(int v, int w, Weight weight)

    {

    assert(v >= 0 && v < n);

    assert(w >= 0 && w < n);

       

    //如果顶点v和顶点w之间已经存在一条边,就删掉,

    //之后按照传入权值重建一条边,即直接覆盖

    if (hasEdge(v, w))

    {

    delete g[v][w];

       

    //如果是无向图,还要删除和主对角线对称的值

    if (!directed)

    {

    delete g[w][v];

    }

       

    m--;

    }

       

    g[v][w] = new Edge<Weight>(v, w, weight);

       

    //如果是无向图,还要在和主对角线对称处添加值

    if (!directed)

    {

    g[w][v] = new Edge<Weight>(w, v, weight);

    }

       

    m++;

    }

       

       

    //hasEdge()判断顶点v和顶点w之间是否有边

    //hasEdge()的时间复杂度:O(1)

    bool hasEdge(int v, int w)

    {

    assert(v >= 0 && v < n);

    assert(w >= 0 && w < n);

    return g[v][w] != NULL;

    }

       

       

    void show()

    {

       

    for (int i = 0; i < n; i++)

    {

    for (int j = 0; j < n; j++)

    {

    if (g[i][j])

    {

    cout << g[i][j]->wt() << " ";

    }

    else

    {

    cout << "NULL ";

    }

    }

    cout << endl;

    }

    }

       

       

    //邻边迭代器(相邻,即 adjacent

    class adjIterator

    {

    private:

       

    DenseGraph &G; //图引用,即要迭代的图

    int v; //顶点v

    int index; //相邻顶点的索引

       

    public:

       

    adjIterator(DenseGraph &graph, int v) : G(graph)

    {

    this->v = v;

    this->index = -1;

    }

       

       

    //要迭代的第一个元素

    Edge<Weight> *begin()

    {

    //找第一个权值不为NULL的元素,即为要迭代的第一个元素

    index = -1;

    return next();

    }

       

       

    //要迭代的下一个元素

    Edge<Weight> *next()

    {

    for (index += 1; index < G.V(); index++)

    {

    if (G.g[v][index])

    {

    return index;

    }

    }

       

    return NULL;

    }

       

       

    //判断迭代是否终止

    bool end()

    {

    return index >= G.V();

    }

    };

    };

       

       

    #endif

       

       

       

    ReadGraph.h:

       

    #ifndef READGRAPH_H

    #define READGRAPH_H

       

    #include <iostream>

    #include <string>

    #include <fstream>

    #include <sstream>

    #include <cassert>

    using namespace std;

       

       

       

    //从文件中读取图的测试用例

    template <typename Graph, typename Weight>

    class ReadGraph

    {

       

    public:

    ReadGraph(Graph &graph, const string &filename)

    {

       

    ifstream file(filename);

    string line; //一行一行的读取

    int V, E;

       

    assert(file.is_open());

       

    //读取file中的第一行到line

    assert(getline(file, line));

    //将字符串line放在stringstream

    stringstream ss(line);

    //通过stringstream解析出整型变量:顶点数和边数

    ss >> V >> E;

       

    //确保文件里的顶点数和图的构造函数中传入的顶点数一致

    assert(V == graph.V());

       

    //读取file中的其它行

    for (int i = 0; i < E; i++)

    {

       

    assert(getline(file, line));

    stringstream ss(line);

       

    int a, b;

    Weight w;

    ss >> a >> b >> w;

    assert(a >= 0 && a < V);

    assert(b >= 0 && b < V);

    graph.addEdge(a, b, w);

    }

    }

    };

       

       

    #endif

       

       

       

    MinIndexHeap.h:

       

    #ifndef MININDEXHEAP_H

    #define MININDEXHEAP_H

       

    #include <iostream>

    #include <string>

    #include <cassert>

    #include <algorithm>

    using namespace std;

       

       

       

    //最小索引堆:索引从0开始

    template<typename Item>

    class MinIndexHeap

    {

       

    private:

    Item *data; //指向存储元素的数组

    int *indexes; //指向存储索引的数组

    int *reverse; //指向存储反向索引的数组

    int count;

    int capacity;

       

       

    //私有函数,用户不能调用

    void shiftUp(int k)

    {

    //如果新添加的元素小于父节点的元素,则进行交换

    while (k > 0 && data[indexes[(k - 1) / 2]] > data[indexes[k]])

    {

    swap(indexes[(k - 1) / 2], indexes[k]);

    reverse[indexes[(k - 1) / 2]] = (k - 1) / 2;

    reverse[indexes[k]] = k;

    k = (k - 1) / 2;

    }

    }

       

       

    //也是私有函数,用户不能调用

    void shiftDown(int k)

    {

    //只要当前节点有孩子就进行循环

    while (2 * k + 1 < count)

    {

    // 在此轮循环中,data[indexes[k]]data[indexes[j]]交换位置

    int j = 2 * k + 1;

       

    // data[indexes[j]]data[indexes[j]]data[indexes[j+1]]中的最小值

    if (j + 1 < count && data[indexes[j + 1]] < data[indexes[j]])

    {

    j += 1;

    }

       

    if (data[indexes[k]] <= data[indexes[j]])

    {

    break;

    }

       

    swap(indexes[k], indexes[j]);

    reverse[indexes[k]] = k;

    reverse[indexes[j]] = j;

    k = j;

    }

    }

       

       

    public:

       

    MinIndexHeap(int capacity)

    {

    data = new Item[capacity];

    indexes = new int[capacity];

    reverse = new int[capacity];

    //初始化reverse数组

    for (int i = 0; i < capacity; i++)

    {

    reverse[i] = -1;

    }

    //计数器,这里索引等于计数器减一

    count = 0;

    this->capacity = capacity;

       

    }

       

       

    ~MinIndexHeap()

    {

    delete []data;

    delete []indexes;

    delete []reverse;

    }

       

       

    int size()

    {

    return count;

    }

       

       

    bool isEmpty()

    {

    return count == 0;

    }

       

       

    void insert(int i, Item item)

    {

    //防止越界

    assert(count <= capacity);

    assert(i >= 0 && i <= capacity);

       

    data[i] = item;

    indexes[count] = i;

    reverse[i] = count;

    count++;

       

    shiftUp(count - 1);

    }

       

       

    //取出最小的data

    Item extractMin()

    {

    //首先要保证堆不为空

    assert(count > 0);

       

    Item ret = data[indexes[0]];

    swap(indexes[0], indexes[count - 1]);

    reverse[indexes[count - 1]] = -1;

    reverse[indexes[0]] = 0;

    count--;

    shiftDown(0);

    return ret;

    }

       

       

    //取出最小的data对应的index

    int extractMinIndex()

    {

    assert(count > 0);

       

    //对于外部来说,索引从0开始,所以要减一

    int ret = indexes[0];

    swap(indexes[0], indexes[count - 1]);

    reverse[indexes[count - 1]] = -1;

    reverse[indexes[0]] = 0;

    count--;

    shiftDown(0);

    return ret;

    }

       

       

    Item getMin()

    {

    assert(count > 0);

    return data[indexes[0]];

    }

       

       

    int getMinIndex()

    {

    assert(count > 0);

    return indexes[0];

    }

       

       

    bool contain(int i){

    assert(i >= 0 && i <= capacity);

    //reverse数组在构造函数中都初始化为-1

    //所以拿-1做比较

    return reverse[i] != -1;

    }

       

       

    Item getItem(int i)

    {

    assert(contain(i));

    //对于外部来说,索引从0开始,

    //对于内部来说,索引从1开始,

    //所以要加一

    return data[i];

    }

       

       

    //修改 index 对应的 data

    void change(int i, Item newItem)

    {

    //防止越界和检查i是否在堆中,

    //因为有可能已经取出去了

    assert(contain(i));

       

    data[i] = newItem;

       

    // 找到indexes[j] = i, j表示data[i]在堆中的位置

    // 之后尝试着shiftUp(j)一下, shiftDown(j)一下

    //看看能不能向上或向下移动以保持堆的性质

    int j = reverse[i];

    shiftUp(j);

    shiftDown(j);

       

    //先用O(1)的时间找到位置,再用O(lgn)的时间完成

    //Shift UpShift Down,此时,该函数的时间复杂

    //度就是O(lgn)级别的,如果有n个堆操作,总时间

    //就是O(n*lgn)

    //

    //加入了反向查找后,性能得到了巨大的提升

    }

       

       

    public:

       

    //在控制台打印测试用例

    void testPrint()

    {

       

    //限制:只能打印100个元素以内的堆,因为控制台一行的字符数量有限

    if (size() >= 100)

    {

    cout << "Fancy print can only work for less than 100 int";

    return;

    }

       

    //限制:只能打印类型是int的堆

    if (typeid(Item) != typeid(int))

    {

    cout << "Fancy print can only work for int item";

    return;

    }

       

    cout << "The Heap size is: " << size() << endl;

    cout << "data in heap: ";

    for (int i = 0; i < size(); i++)

    {

    cout << data[i] << " ";

    }

    cout << endl;

    cout << endl;

       

    int n = size();

    int max_level = 0;

    int number_per_level = 1;

    while (n > 0)

    {

    max_level += 1;

    n -= number_per_level;

    number_per_level *= 2;

    }

       

    int max_level_number = int(pow(2, max_level - 1));

    int cur_tree_max_level_number = max_level_number;

    int index = 0;

    for (int level = 0; level < max_level; level++)

    {

    string line1 = string(max_level_number * 3 - 1, ' ');

       

    int cur_level_number = min(count - int(pow(2, level)) + 1,

    int(pow(2, level)));

       

    bool isLeft = true;

       

    for (int index_cur_level = 0; index_cur_level < cur_level_number;

    index++, index_cur_level++)

    {

    putNumberInLine(indexes[index], line1, index_cur_level,

    cur_tree_max_level_number * 3 - 1, isLeft);

       

    isLeft = !isLeft;

    }

    cout << line1 << endl;

       

       

    if (level == max_level - 1)

    {

    break;

    }

       

       

    string line2 = string(max_level_number * 3 - 1, ' ');

    for (int index_cur_level = 0; index_cur_level < cur_level_number;

    index_cur_level++)

    {

    putBranchInLine(line2, index_cur_level, cur_tree_max_level_number * 3 - 1);

    }

       

    cout << line2 << endl;

       

    cur_tree_max_level_number /= 2;

    }

    }

       

       

       

    private:

       

    void putNumberInLine(int num, string &line, int index_cur_level,

    int cur_tree_width, bool isLeft)

    {

       

    int sub_tree_width = (cur_tree_width - 1) / 2;

       

    int offset = index_cur_level * (cur_tree_width + 1) + sub_tree_width;

       

    assert(offset + 1 < line.size());

       

    if (num >= 10)

    {

    line[offset + 0] = '0' + num / 10;

    line[offset + 1] = '0' + num % 10;

    }

    else

    {

    if (isLeft)

    line[offset + 0] = '0' + num;

    else

    line[offset + 1] = '0' + num;

    }

    }

       

       

    void putBranchInLine(string &line, int index_cur_level, int cur_tree_width)

    {

       

    int sub_tree_width = (cur_tree_width - 1) / 2;

       

    int sub_sub_tree_width = (sub_tree_width - 1) / 2;

       

    int offset_left = index_cur_level * (cur_tree_width + 1) + sub_sub_tree_width;

       

    assert(offset_left + 1 < line.size());

       

    int offset_right = index_cur_level * (cur_tree_width + 1) + sub_tree_width

    + 1 + sub_sub_tree_width;

       

    assert(offset_right < line.size());

       

    line[offset_left + 1] = '/';

    line[offset_right + 0] = '\';

    }

    };

       

       

    #endif

       

       

       

    Dijkstra.h:

       

    #ifndef DIJKSTRA_H

    #define DIJKSTRA_H

       

    #include "Edge.h"

    #include "MinIndexHeap.h"

    #include <iostream>

    #include <vector>

    #include <stack>

    using namespace std;

       

       

       

    //Dijkstra 算法实现最短路径

    template<typename Graph, typename Weight>

    class Dijkstra

    {

       

    private:

       

    Graph &G; //图的引用,即要进行操作的图

    int s; //起始顶点 ss source

    Weight *distTo; //起始顶点 s 到每一个顶点的当前最短距离

    bool *marked; //对已经找到最短路径的顶点进行标识

    vector<Edge<Weight>*> from; //经由哪条边到达了当前顶点

       

    public:

       

    Dijkstra(Graph &graph, int s) :G(graph)

    {

       

    this->s = s;

    distTo = new Weight[G.V()];

    marked = new bool[G.V()];

       

    for (int i = 0; i < G.V(); i++)

    {

    //由于不知道 distTo 数组中元素的具体类型,

    //所以使用模板类型Weight的默认构造函数,

    //如果指定的模板为 int,会被初始化为 0

    distTo[i] = Weight();

    marked[i] = false;

    from.push_back(NULL);

    }

       

    MinIndexHeap<Weight> ipq(G.V());

       

    // Dijkstra

    //

    //对起始顶点 s 到自身的最短距离进行初始化,

    //如果指定的模板为 int,会被初始化为 0

    distTo[s] = Weight();

    ipq.insert(s, distTo[s]);

    marked[s] = true;

       

    //只要最小索引堆不为空,就进行循环

    while (!ipq.isEmpty())

    {

    //从最小索引堆中找到当前最短距离最小的顶点 v

    //此时,distTo[v] 就是起始顶点 s 到顶点 v

    //最短距离

    int v = ipq.extractMinIndex();

    marked[v] = true;

       

    //注意:声明迭代器时,前面还要加 typename,表明

    //adjIterator Graph 中的类型,而不是成员变量

    typename Graph::adjIterator adj(G, v);

    //对当前顶点 v 的所有相邻顶点依次进行松弛操作

    for (Edge<Weight> *e = adj.begin(); !adj.end(); e = adj.next())

    {

    //当前顶点 v 的相邻顶点 w

    int w = e->other(v);

    //如果顶点 w 没有被标识,即从起始顶点

    // s 到相邻顶点 w 的最短路径还没有找到

    if (!marked[w])

    {

    //1)如果还没有边到达相邻顶点 w

    //2)或:"经过"当前顶点 v 到相邻顶点 w 所得到的

    //路径小于"不经过"当前顶点 v 到相邻顶点 w 所得到

    //的路径,就进行松弛操作

    if (from[w] == NULL || distTo[v] + e->wt() < distTo[w])

    {

    distTo[w] = distTo[v] + e->wt();

    from[w] = e;

       

    //判断最小索引堆中是否包含顶点 w

    //如果包含就更新,否则直接插入

    if (ipq.contain(w))

    {

    ipq.change(w, distTo[w]);

    }

    else

    {

    ipq.insert(w, distTo[w]);

    }

    }

    }

    }

    }

    }

       

       

    ~Dijkstra()

    {

    delete []distTo;

    delete []marked;

    }

       

       

    //顶点 s 到顶点 w 的最短距离

    Weight shortestPathTo(int w)

    {

    assert(w >= 0 && w < G.V());

    return distTo[w];

    }

       

       

    //判断顶点 s 到顶点 w 是否有路径,

    //即判断二者是否连通即可

    bool hasPathTo(int w)

    {

    assert(w >= 0 && w < G.V());

    return marked[w];

    }

       

       

    //找到从顶点 s 到顶点 w 的最短路径的边的组成:通过from数组

    //从顶点 w 倒推回去,并存储在栈中,最后再从栈中转存到向量中

    void shortestPath(int w, vector<Edge<Weight>> &vec)

    {

       

    assert(w >= 0 && w < G.V());

       

    stack<Edge<Weight>*> s;

       

    Edge<Weight> *e = from[w];

       

    //直到倒推到起始顶点,对于有向图

    //来说,e->v() 即一条边的起点

    while (e->v() != this->s)

    {

    s.push(e);

    e = from[e->v()];

    }

    s.push(e);

       

    //只要栈不为空,就将栈顶元素放入

    //向量中,并出栈

    while (!s.empty())

    {

    e = s.top();

    vec.push_back(*e);

    s.pop();

    }

    }

       

       

    //打印从顶点 s 到顶点 w 的最短路径

    void showPath(int w)

    {

       

    assert(w >= 0 && w < G.V());

       

    vector<Edge<Weight>> vec;

    shortestPath(w, vec);

    for (int i = 0; i < vec.size(); i++)

    {

    cout << vec[i].v() << " -> ";

    if (i == vec.size() - 1)

    {

    cout << vec[i].w() << endl;

    }

    }

    }

    };

       

       

    #endif

       

       

       

    main.cpp:

       

    #include "SparseGraph.h"

    #include "DenseGraph.h"

    #include "ReadGraph.h"

    #include "Dijkstra.h"

    #include <iostream>

    using namespace std;

       

       

       

    int main()

    {

       

    string filename = "testG1.txt";

    int V = 5;

       

    //稀疏图:通常在实际生活中所处理的图,稀疏图相对更多

    SparseGraph<int> g = SparseGraph<int>(V, true);

    //SparseGraph<int> g = SparseGraph<int>(V, false);

    ReadGraph<SparseGraph<int>, int> readGraph(g, filename);

       

    cout << "Test Dijkstra:" << endl << endl;

    Dijkstra<SparseGraph<int>, int> dij(g, 0);

    for (int i = 1; i < V; i++)

    {

    cout << "Shortest Path to " << i << " : "

    << dij.shortestPathTo(i) << endl;

       

    dij.showPath(i);

       

    cout << "----------" << endl;

    }

       

    system("pause");

    return 0;

    }

       

       

    //每一次插入和更新,都使用 logV 的时间复杂度,而整个

    //Dijkstra 算法过程中,要对所有的边进行一次遍历,最

    //终使得算法的时间复杂度是 O(E*logV) 这个级别的

       

       

    运行一览:

       

       

       

       

    其中,testG1.txt 的内容如下:

       

       

       

    该文件可以分成两个部分:

       

    1)第一行:两个数字分别代表顶点数和边数

       

    2)其它行:每一行的前两个数字表示一条边,第三个数字表示权值

       

       

       

       

       

       

       

       

       

    【made by siwuxie095】

  • 相关阅读:
    反射泛型方法
    Redis令牌桶限流
    laravel中间件的使用
    Laravel-权限系统
    Laravel 即时应用的一种实现方式
    laravel实现多模块
    laravel5+ElasticSearch+go-mysql-elasticsearch MySQL数据实时导入(mac)
    swoole视频直播
    开发SSO单点登录需要注意的问题
    进程、线程、协程三者之间的联系与区别
  • 原文地址:https://www.cnblogs.com/siwuxie095/p/7135594.html
Copyright © 2011-2022 走看看