参考:图论最短距离(Shortest Path)算法动画演示-Dijkstra(迪杰斯特拉)和Floyd(弗洛伊德)

最短路径的概念

  • 如果从有向图中某一顶点(起始顶点)到达另一顶点(终止顶点)的路径不止一条,那么如何找到一条路径使得此路径各边上的权值总和达到最小?

image.png

问题

给定一个带权有向图 G 与起始顶点 v ,求从 v 到 G 中其它顶点的最短路径 (每条边上都存在有意义的权值)

image.png

解决思路

  • Dijkstra 提出按照路径长度的递增次序,逐步产生最短路径

首先求出长度最短的一条最短路径,再参照它求出长度次短的一条最短路径,依次类推,直到从起始顶点 v 到其它各顶点的最短路径全部求出为止

image.png

准备工作

  • 辅助数组: Array<E> dist

    • dist[i] 表示当前从起始顶点 v0到顶点 vi 的路径长度
  • 初始设置

    • 若从起始顶点 v0 到顶点 vi 有边:dist[i] 为该边上的权值
    • 若从起始顶点 v0 到顶点 vi`无边: dist[i] 为

实例分析:Dijkstra 算法演示

1_meitu_1.jpg

2_meitu_2.jpg

3_meitu_3.jpg

4_meitu_4.jpg

Dijkstra 算法步骤

  1. 初始化: S ← {v0}

    • dist[j] ← Edge[0][j], j=1,2,...,n-1;
  2. 找出最小路径值所对应的顶点 k:

    • dist[k] == min{ dist[i] }, i ∈ V - S;
    • S ← S U {k};// 标记 k 顶点进入 S 集合
  3. 对于每一个 i ∈ V - S 修改:

    • dist[i] ← min{dist[i], dist[k] + Edge[k][i]}
  4. 判断:若 S == V,则算法结束,否则转 2

Dijkstra 算法精髓

  • S 集合内的顶点是已经找到最短路径的顶点
  • V0 到 w 的最短路径只能通过 S 集合内的顶点

image.png

dist[w] 可能改变:
if (dist[u] + Edge[u, w] < dist[w])
{
    dist[w] = dist[u] + Edge[u, w];
}

image.png

问题

如何记录最短路径上的各个顶点?

定义辅助数组

  • Array<int> path;

    • path[i] 表示当前路径上顶点 i 的前驱顶点
    • 初始化:path = {-1}
修改:
if (dist[u] + Edge[u, w] < dist[w])
{
    dist[w] = dist[u] + Edge[u, w];
    path[w] = u;
}

image.png

步骤描述:

0_meitu_1.jpg

1_meitu_2.jpg

2_meitu_3.jpg

3_meitu_4.jpg

4_meitu_5.jpg

5_meitu_6.jpg

编程实验:最短路径算法

文件:Graph.h

#ifndef GRAPH_H
#define GRAPH_H

#include "Object.h"
#include "SharedPointer.h"
#include "DynamicArray.h"
#include "LinkQueue.h"
#include "LinkStack.h"
#include "Sort.h"

namespace DTLib
{

template <typename E>
struct Edge : public Object
{
    int b;
    int e;
    E data;

    Edge(int i=-1, int j=-1)  : b(i), e(j)
    {
    }

    Edge(int i, int j, const E &value) : b(i), e(j), data(value)
    {
    }

    bool operator == (const Edge &obj)
    {
        return (b == obj.b) && (e == obj.e);
    }

    bool operator != (const Edge &obj)
    {
        return !(*this == obj);
    }

    bool operator < (const Edge &obj)
    {
        return (data < obj.data);
    }

    bool operator > (const Edge &obj)
    {
        return (data > obj.data);
    }
};

template <typename V, typename E>
class Graph : public Object
{
public:
    virtual V getVertex(int i) const = 0;
    virtual bool getVertex(int i, V &value) const = 0;
    virtual bool setVertex(int i, const V &value) = 0;
    virtual SharedPointer<Array<int>> getAdjacent(int i) const = 0;
    virtual bool isAdjacent(int i, int j) const = 0;
    virtual E getEdge(int i, int j) const = 0;
    virtual bool getEdge(int i, int j, E &value) const = 0;
    virtual bool setEdge(int i, int j, const E &value) = 0;
    virtual bool removeEdge(int i, int j) = 0;
    virtual int vCount() const = 0;
    virtual int eCount() = 0;
    virtual int OD(int i) = 0;
    virtual int ID(int i) = 0;
    virtual int TD(int i)
    {
        return OD(i) + ID(i);
    }

    bool asUndirected()
    {
        bool ret = true;

        for (int i=0; i<vCount() && ret; ++i)
        {
            for (int j=0; j<vCount() && ret; ++j)
            {
                if (isAdjacent(i, j))
                {
                    ret = isAdjacent(j, i) && (getEdge(i, j) == getEdge(j, i));
                }
            }
        }

        return ret;
    }

    SharedPointer<Array<int>> BFS(int i)
    {
        DynamicArray<int> *ret = nullptr;

        if ((0 <= i) && (i < vCount()))
        {
            LinkQueue<int> q;
            LinkQueue<int> r;
            DynamicArray<bool> visited(vCount());

            for (int j=0; j<visited.length(); ++j)
            {
                visited[j] = false;
            }

            q.add(i);

            while (q.length() > 0)
            {
                int v = q.front();

                q.remove();

                if (!visited[v])
                {
                    SharedPointer<Array<int>> aj = getAdjacent(v);

                    for (int j=0; j<aj->length(); ++j)
                    {
                        q.add((*aj)[j]);
                    }

                    r.add(v);

                    visited[v] = true;
                }
            }

            ret = toArray(r);
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invalid ...");
        }

        return ret;
    }

#ifdef DFS_R
    SharedPointer<Array<int>> DFS(int i)  // 递归版深度优先遍历
    {
        DynamicArray<int> *ret = nullptr;

        if ((0 <= i) && (i < vCount()))
        {
            LinkQueue<int> r;
            DynamicArray<bool> visited(vCount());

            for (int j=0; j<vCount(); ++j)
            {
                visited[j] = false;
            }

            DFP(i, visited, r);

            ret = toArray(r);
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invalid ...");
        }

        return ret;
    }
#else
    SharedPointer<Array<int>> DFS(int i)
    {
        DynamicArray<int> *ret = nullptr;

        if ((0 <= i) && (i < vCount()))
        {
            LinkStack<int> s;
            LinkQueue<int> r;
            DynamicArray<bool> visited(vCount());

            for (int j=0; j<visited.length(); ++j)
            {
                visited[j] = false;
            }

            s.push(i);

            while (s.size() > 0)
            {
                int v = s.top();

                s.pop();

                if (!visited[v])
                {
                    SharedPointer<Array<int>> aj = getAdjacent(v);

                    for (int j=aj->length()-1; j>=0; --j)
                    {
                        s.push((*aj)[j]);
                    }

                    r.add(v);

                    visited[v] = true;
                }
            }

            ret = toArray(r);
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterExcetion, "Parameter i is invalid ...");
        }

        return ret;
    } 

#endif

    SharedPointer<Array<Edge<E>>> prim(const E &LIMIT, const bool MINIMUM = true)
    {
        LinkQueue<Edge<E>> ret;

        if (asUndirected())
        {
            DynamicArray<int> adjVex(vCount());
            DynamicArray<bool> mark(vCount());
            DynamicArray<E> cost(vCount());
            SharedPointer<Array<int>> aj = nullptr;
            bool end = false;
            int v = 0;

            for (int i=0; i<vCount(); ++i)
            {
                adjVex[i] = -1;
                mark[i] = false;
                cost[i] = LIMIT;
            }

            mark[v] = true;

            aj = getAdjacent(v);

            for (int i=0; i<aj->length(); ++i)
            {
                cost[(*aj)[i]] = getEdge(v, (*aj)[i]);
                adjVex[(*aj)[i]] = v;
            }

            for (int i=0; i<vCount() && !end; ++i)
            {
                E m = LIMIT;
                int k = -1;

                for (int j=0; j<vCount(); ++j)
                {
                    if (!mark[j] && (MINIMUM ? (m > cost[j]) : (m < cost[j])))
                    {
                        m = cost[j];
                        k = j;
                    }
                }

                end = (k == -1);

                if (!end)
                {
                    ret.add(Edge<E>(adjVex[k],k, getEdge(adjVex[k],k)));

                    mark[k] = true;

                    aj = getAdjacent(k);

                    for (int j=0; j<aj->length(); ++j)
                    {
                        if (!mark[(*aj)[j]] && (MINIMUM ? (getEdge(k, (*aj)[j]) < cost[(*aj)[j]]) : (getEdge(k, (*aj)[j]) > cost[(*aj)[j]])))
                        {
                            cost[(*aj)[j]] = getEdge(k, (*aj)[j]);

                            adjVex[(*aj)[j]] = k;
                        }
                    }
                }
            }
        }
        else
        {
            THROW_EXCEPTION(InvalidOpertionExcetion, "Prim operator is for undirected grap only ...");
        }

        if (ret.length() != (vCount() - 1))
        {
            THROW_EXCEPTION(InvalidOpertionExcetion, "No enough edge for prim operation ...");
        }

        return toArray(ret);
    }

    SharedPointer<Array<Edge<E>>> Kruskal( const bool MINIMUM = true)
    {
        LinkQueue<Edge<E>> ret;
        DynamicArray<int> p(vCount());
        SharedPointer<Array<Edge<E>>> edges = getUndirectedEdges();

        for (int i=0; i<p.length(); ++i)
        {
            p[i] = -1;
        }

        Sort::Shell(*edges, MINIMUM);

        for (int i=0; (i<edges->length()) && (ret.length() < (vCount()-1)); ++i)
        {
            int b = find(p, (*edges)[i].b);
            int e = find(p, (*edges)[i].e);

            if (b != e)
            {
                p[e] = b;

                ret.add((*edges)[i]);
            }
        }

        if (ret.length() != vCount() - 1)
        {
            THROW_EXCEPTION(InvalidOpertionExcetion, "No enough edges for Kruskal operation ...");
        }

        return toArray(ret);
    }

    SharedPointer<Array<int>> dijkstra(int i, int j, const E &LIMIT)
    {
        LinkQueue<int> ret;

        if ((0 <= i) && (i < vCount()) && (0 <= j) && (j < vCount()))
        {
            DynamicArray<E> dist(vCount());
            DynamicArray<int> path(vCount());
            DynamicArray<bool> mark(vCount());

            for (int k=0; k<vCount(); ++k)
            {
                mark[k] = false;
                path[k] = -1;

                dist[k] = isAdjacent(i, k) ? (path[k]=i, getEdge(i, k)) : LIMIT;
            }

            mark[i] = true;

            for (int k=0; k<vCount(); ++k)
            {
                E m = LIMIT;
                int u = -1;

                for (int w=0; w<vCount(); ++w)
                {
                    if (!mark[w] && dist[w] < m)
                    {
                        m = dist[w];
                        u = w;
                    }
                }

                if (u == -1)
                {
                    break;
                }

                mark[u] = true;

                for (int w=0; w<vCount(); ++w)
                {
                    if (!mark[w] && isAdjacent(u, w) && (dist[u] + getEdge(u, w) < dist[w]))
                    {
                        dist[w] = dist[u] + getEdge(u, w);
                        path[w] = u;
                    }
                }
            }

            LinkStack<int> s;

            s.push(j);

            for (int k=path[j]; k!=-1; k=path[k])
            {
                s.push(k);
            }

            while (s.size() > 0)
            {
                ret.add(s.top());

                s.pop();
            }
        }
        else
        {
            THROW_EXCEPTION(InvalidOpertionExcetion, "Parameter <i, j> is invalid ...");
        }

        if (ret.length() < 2)
        {
            THROW_EXCEPTION(ArithmeticExcption, "This is no path from i to j ...");
        }

        return toArray(ret);
    }

protected:
    template <typename T>
    DynamicArray<T>* toArray(LinkQueue<T> &queue)
    {
        DynamicArray<T> *ret = new DynamicArray<T>(queue.length());

        if (ret != nullptr)
        {
            for (int i=0; i<ret->length(); ++i, queue.remove())
            {
                ret->set(i, queue.front());
            }
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create ret obj ...");
        }

        return ret;
    }

#ifdef DFS_R
    void DFP(int i, DynamicArray<bool> &visited, LinkQueue<int>& queue)
    {
        if (!visited[i])
        {
            queue.add(i);

            visited[i] = true;

            SharedPointer<Array<int>> aj = getAdjacent(i);

            for (int j=0; j<aj->length(); ++j)
            {
                DFP((*aj)[j], visited, queue);
            }
        }
    }
#endif

    int find(Array<int> &p, int v)
    {
        while (p[v] != -1)
        {
            v = p[v];
        }

        return v;
    }

    SharedPointer<Array<Edge<E>>> getUndirectedEdges()
    {
        DynamicArray<Edge<E>> *ret = nullptr;

        if (asUndirected())
        {
            LinkQueue<Edge<E>> queue;

            for (int i=0; i<vCount(); ++i)
            {
                for (int j=0; j<vCount(); ++j)
                {
                    if (isAdjacent(i, j))
                    {
                        queue.add(Edge<E>(i, j, getEdge(i, j)));
                    }
                }
            }

            ret = toArray(queue);
        }
        else
        {
            THROW_EXCEPTION(InvalidOpertionExcetion, "This function is for undirected graph only ...");
        }

        return ret;
    }
};

}

#endif // GRAPH_H

文件:main.cpp

#include <iostream>
#include "MatrixGraph.h"
#include "ListGraph.h"

using namespace std;
using namespace DTLib;

template< typename V, typename E >
Graph<V, E>& GraphEasy()
{
    static MatrixGraph<4, V, E> g;

    g.setEdge(0, 1, 1);
    g.setEdge(0, 2, 3);
    g.setEdge(1, 2, 1);
    g.setEdge(1, 3, 4);
    g.setEdge(2, 3, 1);

    return g;
}

template< typename V, typename E >
Graph<V, E>& GraphComplex()
{
    static ListGraph<V, E> g(5);

    g.setEdge(0, 1, 10);
    g.setEdge(0, 3, 30);
    g.setEdge(0, 4, 100);

    g.setEdge(1, 2, 50);

    g.setEdge(2, 4, 10);

    g.setEdge(3, 2, 20);
    g.setEdge(3, 4, 60);

    return g;
}

void func1()
{
    Graph<int, int>& g = GraphEasy<int, int>();

    SharedPointer< Array<int> > p = g.dijkstra(0, 3, 65535);

    for(int i=0; i<p->length(); i++)
    {
        cout << (*p)[i] << " ";
    }

    cout << endl;
}

void func2()
{
    Graph<int, int>& g = GraphComplex<int, int>();
    SharedPointer< Array<int> > p = g.dijkstra(0, 4, 65535);

    for(int i=0; i<p->length(); i++)
    {
        cout << (*p)[i] << " ";
    }

    cout << endl;
}


int main()
{
    func1();

    func2();

    return 0;
}

输出:

0 1 2 3
0 3 2 4

小结

  • Dijkstra 最短路径算法是基于递推的思想完成的
  • 起始顶点到其它各顶点的最短路径通过动态推导得到
  • 未标记顶点的最短路径只能由以标记顶点计算得出
  • 算法的最终结果是起始顶点到其他各顶点的最短路径

以上内容整理于狄泰软件学院系列课程,请大家保护原创!


TianSong
737 声望139 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧