【数据结构】79_Folyd最短路径

问题的提法

  • 已知一个各边权值均大于 0 的带权有向图,对每一对顶点 vi ≠ vj,求出 vi 与 vj 之间的最短路径值以及最短路径上的顶点。

Floyd 算法核心

  • 定义一个 n 阶方阵序列:

    • A-1, A0,..., An-1
  • 其中:

    • A-1[i][j] = Edge[i][j]
    • Ak[i][j] = min{ Ak-1[i][j], Ak-1[i][k] + Ak-1[k][j]}
    • k = 0, 1, ..., n-1

【数据结构】79_Folyd最短路径_第1张图片

n 阶方阵中元素的意义

  • A-1[i][j]: vi 到 vj 的权值,无中间顶点
  • A0[i][j]: vi 到 vj 的路径长度,路径的中间顶点为 0
  • A1[i][j]: vi 到 vj 的路径长度, 路径的中间顶点可能为0或1
  • ......
  • Ak[i][j]: vi 到 vj 的路径长度, 路径的中间顶点编号不大于k
  • ......
  • An-1[i][j]: vi 到 vj 的最短路径长度

Floyd 算法实例

【数据结构】79_Folyd最短路径_第2张图片

【数据结构】79_Folyd最短路径_第3张图片

【数据结构】79_Folyd最短路径_第4张图片

【数据结构】79_Folyd最短路径_第5张图片

Floyd 算法精髓

  • An-1 定义为邻接矩阵,则:

    • A0, ...,An 通过中转顶点逐一递推得到
  • Ak 矩阵中元素的更新

    • Ak[i][j] = min{ Ak-1[i][j], Ak-1[i][k] + Ak-1[k][j] }
  • A 矩阵的推导就是最短路径的推导

    • A[i][j] 为 i 到 j 的路径值,在推导过程中逐步减小

Floyd 算法的实现

初始化

本质: 使用邻接矩阵初始化 A -1
for(int i=0; i

A0, ..., An-1 矩阵推导

本质:使用中转顶点逐步推导最短路径
for (int k=0; k

编程实验:最短路径算法

文件: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 
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 
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> 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> BFS(int i)
    {
        DynamicArray *ret = nullptr;

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

            for (int j=0; j 0)
            {
                int v = q.front();

                q.remove();

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

                    for (int j=0; jlength(); ++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> DFS(int i)  // 递归版深度优先遍历
    {
        DynamicArray *ret = nullptr;

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

            for (int j=0; j> DFS(int i)
    {
        DynamicArray *ret = nullptr;

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

            for (int j=0; j 0)
            {
                int v = s.top();

                s.pop();

                if (!visited[v])
                {
                    SharedPointer> 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>> prim(const E &LIMIT, const bool MINIMUM = true)
    {
        LinkQueue> ret;

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

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

            for (int i=0; i cost[j]) : (m < cost[j])))
                    {
                        m = cost[j];
                        k = j;
                    }
                }

                end = (k == -1);

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

                    mark[k] = true;

                    aj = getAdjacent(k);

                    for (int j=0; jlength(); ++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>> Kruskal( const bool MINIMUM = true)
    {
        LinkQueue> ret;
        DynamicArray p(vCount());
        SharedPointer>> edges = getUndirectedEdges();

        for (int i=0; ilength()) && (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> dijkstra(int i, int j, const E &LIMIT)
    {
        LinkQueue ret;

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

            for (int k=0; k 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  is invalid ...");
        }

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

        return toArray(ret);
    }

    int floyd(int x, int y, const E &LIMIT)
    {
        int ret = -1;

        if ((0 <= x) && (x < vCount())  && (0 <= y) && (y < vCount()))
        {
            DynamicArray> dist(vCount());

            for (int k=0; k (dist[i][k] + dist[k][j]))
                        {
                            dist[i][j] = dist[i][k] + dist[k][j];
                        }
                    }
                }
            }

            ret = dist[x][y];
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterExcetion, "Parameter  is invalid ...");
        }

        return ret;
    }


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

        if (ret != nullptr)
        {
            for (int i=0; ilength(); ++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 &visited, LinkQueue& queue)
    {
        if (!visited[i])
        {
            queue.add(i);

            visited[i] = true;

            SharedPointer> aj = getAdjacent(i);

            for (int j=0; jlength(); ++j)
            {
                DFP((*aj)[j], visited, queue);
            }
        }
    }

#endif

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

        return v;
    }

    SharedPointer>> getUndirectedEdges()
    {
        DynamicArray> *ret = nullptr;

        if (asUndirected())
        {
            LinkQueue> queue;

            for (int i=0; i(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 
#include "MatrixGraph.h"
#include "ListGraph.h"

using namespace std;
using namespace DTLib;

template< typename V, typename E >
Graph& 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& GraphComplex()
{
    static ListGraph 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;
}

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

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

    g.setEdge(1, 2, 2);
    g.setEdge(1, 0, 6);

    g.setEdge(2, 0, 3);

    return g;
}

int main()
{
    Graph &g = GraphSample();

    for (int i=0; i

输出:

9 4 6
5 9 2
3 7 9

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

定义辅助矩阵

  • int pathN; // 路径矩阵

    • path[i][j] 表示 i 到 j 的路径上所经过的第 1 个顶点
    • 初始化:

      • path[i][j] = -1; 无边直接连接时
      • path[i][j] = j; 有边直接连接时
修改:
if ((dist[i][k] + dist[k][j]) < dist[i][j])
{
    dist[i][j] = dist[i][k] + dist[k][j];
    path[i][j] = path[i][k];
}

【数据结构】79_Folyd最短路径_第6张图片

说明:

当已找到最短路径[0, 3]: 0 → 1 → 2 → 3,那么所经过的第一个顶点为 1;
也因此可得最短路径[0, 2]:0 → 1 → 2, 那么所经过的第一个顶点为1
因此:path[i][j] = path[i][k];

路径矩阵示例

初始:
dist[0][1] = 1;    path[0][1] = 1;
dist[0][2] = 3;    path[0][2] = 2;
dist[1][2] = 1;    path[1][2] = 2
推导:

因为:
dist[0][1] + dist[1][2] < dist[0][2]
所以:
dist[0][2] = dist[0][1] + dist[1][2]
path[0][2] = path[0][1]


最短路径顶点推导

【数据结构】79_Folyd最短路径_第7张图片

// x 起始顶点
// y 终止顶点
while ((x != -1) && (x != y))
{
    ret.add(x);
    x = path[x][y];
}

if (x != -1)
{
    ret.add(x);
}

编程实验: Floyd 算法的改善

文件: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 
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 
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> 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> BFS(int i)
    {
        DynamicArray *ret = nullptr;

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

            for (int j=0; j 0)
            {
                int v = q.front();

                q.remove();

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

                    for (int j=0; jlength(); ++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> DFS(int i)  // 递归版深度优先遍历
    {
        DynamicArray *ret = nullptr;

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

            for (int j=0; j> DFS(int i)
    {
        DynamicArray *ret = nullptr;

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

            for (int j=0; j 0)
            {
                int v = s.top();

                s.pop();

                if (!visited[v])
                {
                    SharedPointer> 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>> prim(const E &LIMIT, const bool MINIMUM = true)
    {
        LinkQueue> ret;

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

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

            for (int i=0; i cost[j]) : (m < cost[j])))
                    {
                        m = cost[j];
                        k = j;
                    }
                }

                end = (k == -1);

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

                    mark[k] = true;

                    aj = getAdjacent(k);

                    for (int j=0; jlength(); ++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>> Kruskal( const bool MINIMUM = true)
    {
        LinkQueue> ret;
        DynamicArray p(vCount());
        SharedPointer>> edges = getUndirectedEdges();

        for (int i=0; ilength()) && (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> dijkstra(int i, int j, const E &LIMIT)
    {
        LinkQueue ret;

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

            for (int k=0; k 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  is invalid ...");
        }

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

        return toArray(ret);
    }

    SharedPointer> floyd(int x, int y, const E &LIMIT)
    {
        LinkQueue ret;

        if ((0 <= x) && (x < vCount())  && (0 <= y) && (y < vCount()))
        {
            DynamicArray> dist(vCount());
            DynamicArray> path(vCount());

            for (int k=0; k (dist[i][k] + dist[k][j]))
                        {
                            dist[i][j] = dist[i][k] + dist[k][j];
                            path[i][j] = path[i][k];
                        }
                    }
                }
            }

            while ((x != -1) && (x != y))
            {
                ret.add(x);
                x = path[x][y];
            }

            if (x != -1)
            {
                ret.add(x);
            }
        }
        else
        {
            THROW_EXCEPTION(InvalidParameterExcetion, "Parameter  is invalid ...");
        }

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

        return toArray(ret);
    }


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

        if (ret != nullptr)
        {
            for (int i=0; ilength(); ++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 &visited, LinkQueue& queue)
    {
        if (!visited[i])
        {
            queue.add(i);

            visited[i] = true;

            SharedPointer> aj = getAdjacent(i);

            for (int j=0; jlength(); ++j)
            {
                DFP((*aj)[j], visited, queue);
            }
        }
    }

#endif

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

        return v;
    }

    SharedPointer>> getUndirectedEdges()
    {
        DynamicArray> *ret = nullptr;

        if (asUndirected())
        {
            LinkQueue> queue;

            for (int i=0; i(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 
#include "MatrixGraph.h"
#include "ListGraph.h"

using namespace std;
using namespace DTLib;

template< typename V, typename E >
Graph& 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& GraphComplex()
{
    static ListGraph 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;
}

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

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

    g.setEdge(1, 2, 2);
    g.setEdge(1, 0, 6);

    g.setEdge(2, 0, 3);

    return g;
}

void func1()
{
    Graph &g = GraphSample();

    SharedPointer> r = g.floyd(0, 2, 65535);

    for (int i=0; ilength(); ++i)
    {
        cout << (*r)[i] << " ";
    }

    cout << endl;
}

void func2()
{
    Graph &g = GraphComplex();

    SharedPointer> r = g.floyd(0, 4, 65535);

    for (int i=0; ilength(); ++i)
    {
        cout << (*r)[i] << " ";
    }

    cout << endl;
}

int main()
{
    func1();
    func2();

    return 0;
}

输出:

0 1 2
0 3 2 4

小结

  • Floyd 算法通过递推逐步求得所有顶点间的路径
  • Floyd 算法的本质是通过中转顶点寻求更短的路径
  • 邻接矩阵是最短路径推导的起始矩阵
  • 路径矩阵记录了最短路径上的各个点

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

你可能感兴趣的:(c++)