图论(四)最短路算法Dantjig的实现

1、建图

const int maxNum = 0x3f3f3f3f;  // 定义一个足够大的数,代表图中两顶点间无边
class graph
{
private:
    int V;
public:
    vector > g;
    graph(int V)
    {
        this->V = V;
        // g为二维数组,里面每个元素初始化为无穷大
        g = vector >(V, vector(V, maxNum));
    }
    ~graph(){};

    int getV() { return V; }
    void addEdge(int s, int t, int w);
};
void graph::addEdge(int s, int t, int w) {
    g[s][t] = w;
    g[t][s] = w;
}

\quad 图有V个顶点,默认标号为0到V-1。这样,我们就可以通过调用addEdge给图添加从是s点到t点权重为w的一条边。
如果我们要建立如下图所示这样一个图,我们把a点看作0,b点看作7,则建图过程为:
图论(四)最短路算法Dantjig的实现_第1张图片

int main()
{
    graph G(8);
    G.addEdge(0, 1, 2);
    G.addEdge(0, 2, 8);
    G.addEdge(0, 3, 1);
    G.addEdge(1, 2, 6);
    G.addEdge(1, 4, 1);
    G.addEdge(2, 3, 7);
    G.addEdge(2, 4, 4);
    G.addEdge(2, 5, 2);
    G.addEdge(2, 6, 2);
    G.addEdge(3, 6, 9);
    G.addEdge(4, 5, 3);
    G.addEdge(4, 7, 9);
    G.addEdge(5, 6, 4);
    G.addEdge(5, 7, 6);
    G.addEdge(6, 7, 2);
    return 0;
}

2、初始化和大体框架

\quad 我们先来看看初始化和大体框架,最后再来写算法的核心部分。

class Dantjig
{
private:
    graph &G;
    int v;  // 起始点
    bool *visited;  // 判断顶点是否已标号
    int *t;  // 顶点的标号值,表示初始点到当前点的最短路长度
    vector A;  // 访问过的顶点集合
    vector > T; // 访问过的边集合,用于输出路劲
public:
    Dantjig(graph &graph, int v): G(graph)
    {
        // 初始化各个变量
        visited = new bool[G.getV()];
        t = new int[G.getV()];
        for (int i = 0; i < G.getV(); ++i) {
            visited[i] = false;
            t[i] = 0;
        }
        this->v = v;
        A.clear();
        
        _Dantjig(v); // 算法核心
    }
    ~Dantjig()
    {
        delete[] visited;
        delete[] t;
        A.clear();
        T.clear();
    }
    void _Dantjig(int v);  // 申明算法

    // 返回v点到w点的最短距离
    int minLen(int w)
    {
        return t[w];
    }

    // 找出顶点to对应的另一个顶点from
    int getfrom(int to)
    {
        for (int i = 0; i < T.size(); ++i) {
            if(T[i].second==to) return T[i].first;
        }
    }
    
    // 打印路径
    void showpath(int w)
    {
        stack s;
        int from = getfrom(w);
        while(from!=v)
        {
            s.push(from);
            from = getfrom(from);
        }
        s.push(v);
        while(!s.empty())
        {
            cout << s.top() << "->";
            s.pop();
        }
        cout << w << endl;
    }
};

\quad 在类的成员变量中A是已被标记的点,T是访问过的边的集合。上图中如果以a点为起点,b点为终点输入,运形程序后, T = { ( 0 , 3 ) ( 0 , 1 ) ( 1 , 4 ) ( 4 , 5 ) ( 4 , 2 ) ( 2 , 6 ) ( 6 , 7 ) } T=\{(0,3) (0,1) (1,4) (4,5) (4,2) (2,6) (6,7)\} T={(0,3)(0,1)(1,4)(4,5)(4,2)(2,6)(6,7)}。getfrom函数就是找出T中一条边中后面一个点的前一个点。比如要找出7这个点对应的点6,就可以用getfrom得到。showpath即可从终点开始,在T中不断搜索前一个节点,存在栈stack中,之后输出即为起点到终点的路劲。

3、算法核心

void Dantjig::_Dantjig(int v) {
    A.push_back(v);
    if(A.size()==G.getV())  // 图中所有顶点均已标记,结束递归
        return;
    visited[v] = true;
    int minWeight = maxNum; // 存储当前结点下相邻节点中拥有的最短的边长度
    int minV;  // 存放下一个被标记的顶点
    int from, to;  // 记录路劲
    for (int i = 0; i < A.size(); ++i) {
        for (int j = 0; j < G.getV(); ++j) {
            if(!visited[j] && G.g[A[i]][j]!=maxNum)
            {
                int temp = t[A[i]];
                if(temp+G.g[A[i]][j] < minWeight)
                {
                    minWeight = temp+G.g[A[i]][j];
                    minV = j;
                    from = A[i];
                    to = j;
                }
            }
        }
    }
    T.push_back(make_pair(from, to));
    t[minV] = minWeight;  // 更新起始点到该点的最短路劲值
    _Dantjig(minV);   // 递归调用即可
}

4、所有程序如下:

//
// Created by 程勇 on 2019/3/7.
//

#include
using namespace std;

const int maxNum = 0x3f3f3f3f;  // 定义一个足够大的数,代表图中两顶点间无边
class graph
{
private:
    int V;
public:
    vector > g;
    graph(int V)
    {
        this->V = V;
        // g为二维数组,里面每个元素初始化为无穷大
        g = vector >(V, vector(V, maxNum));
    }
    ~graph(){};

    int getV() { return V; }
    void addEdge(int s, int t, int w);
};

void graph::addEdge(int s, int t, int w) {
    g[s][t] = w;
    g[t][s] = w;
}

class Dantjig
{
private:
    graph &G;
    int v;  // 起始点
    bool *visited;  // 判断顶点是否已标号
    int *t;  // 顶点的标号值,表示初始点到当前点的最短路长度
    vector A;  // 访问过的顶点集合
    vector > T; // 访问过的边集合,用于输出路劲
public:
    Dantjig(graph &graph, int v): G(graph)
    {
        // 初始化各个变量
        visited = new bool[G.getV()];
        t = new int[G.getV()];
        for (int i = 0; i < G.getV(); ++i) {
            visited[i] = false;
            t[i] = 0;
        }
        this->v = v;
        A.clear();

        _Dantjig(v); // 算法核心
    }
    ~Dantjig()
    {
        delete[] visited;
        delete[] t;
        A.clear();
        T.clear();
    }
    void _Dantjig(int v);  // 申明算法

    // 返回v点到w点的最短距离
    int minLen(int w)
    {
        return t[w];
    }

    // 找出顶点to对应的另一个顶点from
    int getfrom(int to)
    {
        for (int i = 0; i < T.size(); ++i) {
            if(T[i].second==to) return T[i].first;
        }
    }

    // 打印路径
    void showpath(int w)
    {
        stack s;
        int from = getfrom(w);
        while(from!=v)
        {
            s.push(from);
            from = getfrom(from);
        }
        s.push(v);
        while(!s.empty())
        {
            cout << s.top() << "->";
            s.pop();
        }
        cout << w << endl;
    }
};

void Dantjig::_Dantjig(int v) {
    A.push_back(v);
    if(A.size()==G.getV())  // 图中所有顶点均已标记,结束递归
        return;
    visited[v] = true;
    int minWeight = maxNum; // 存储当前结点下相邻节点中拥有的最短的边长度
    int minV;  // 存放下一个被标记的顶点
    int from, to;  // 记录路劲
    for (int i = 0; i < A.size(); ++i) {
        for (int j = 0; j < G.getV(); ++j) {
            if(!visited[j] && G.g[A[i]][j]!=maxNum)
            {
                int temp = t[A[i]];
                if(temp+G.g[A[i]][j] < minWeight)
                {
                    minWeight = temp+G.g[A[i]][j];
                    minV = j;
                    from = A[i];
                    to = j;
                }
            }
        }
    }
    T.push_back(make_pair(from, to));
    t[minV] = minWeight;  // 更新起始点到该点的最短路劲值
    _Dantjig(minV);   // 递归调用即可
}

int main()
{
    graph G(8);
    G.addEdge(0, 1, 2);
    G.addEdge(0, 2, 8);
    G.addEdge(0, 3, 1);
    G.addEdge(1, 2, 6);
    G.addEdge(1, 4, 1);
    G.addEdge(2, 3, 7);
    G.addEdge(2, 4, 4);
    G.addEdge(2, 5, 2);
    G.addEdge(2, 6, 2);
    G.addEdge(3, 6, 9);
    G.addEdge(4, 5, 3);
    G.addEdge(4, 7, 9);
    G.addEdge(5, 6, 4);
    G.addEdge(5, 7, 6);
    G.addEdge(6, 7, 2);

    Dantjig dantjig(G, 0);
    // 输出a到b(即0到7)的最短距离
    cout << dantjig.minLen(7) << endl;
    dantjig.showpath(7); // 打印路径
    return 0;
}

运行结果如下:
图论(四)最短路算法Dantjig的实现_第2张图片

你可能感兴趣的:(图论)