Johnson算法

该算法主要流程如下:

1、构建有向图

2、往构建的有向图中新增虚拟节点,从该虚拟节点出发连接各点,权重设置为0

3、利用Bellman-Ford算法,算出新增虚拟节点到各点的最短距离,放入节点内部

4、对各边重新赋值,赋值公式为 “原权值+出发点节点内部的值-终点节点内部的值”

5、去掉虚拟节点,留下的图即放入Dijkstra的图

6、接着跑Dijkstra算法,每次选用一个点为起点,算出该点到其他各点的最短距离。每次算完后再轮换起点的节点。可以得到一个n*n的矩阵,其中n为点数。矩阵中的值为最短路径。

7、结果矩阵 = Dijkstra算法得到的矩阵中的每个值+该路径终点节点内部的值-该路径出发点的节点内部的值。

 代码块:

构建有向图

/创建有向图
Graph CreateGraph(vector>matrix)    //matrix的每行结构为[出发点,终点,边权值]
{
    Graph graph;
    for (int i = 0; i < matrix.size(); i++)
    {
        int from = matrix[i][0];
        int to = matrix[i][1];
        int weight = matrix[i][2];
        if(graph.nodes.find(from) == graph.nodes.end())
        {
            graph.nodes[from] =* new Node(from);
        }
        if (graph.nodes.find(to) == graph.nodes.end())
        {
            graph.nodes[to] = *new Node(to);
        }

        graph.nodes[from].nexts.push_back(graph.nodes[to]);//出发点指向的点中 加入 终点
        graph.nodes[from].out++;//出发点出度++
        graph.nodes[to].in++;//终点入度++

        Edge newEdge(weight, graph.nodes[from], graph.nodes[to]);//构造当前边
        graph.edges.push_back(newEdge);
        graph.nodes[from].edges.push_back(newEdge);//出发点的边集中加入当前边
    }
    return graph;
}

新增虚拟节点

//新增虚拟节点
Graph CreateNewGraph(Graph graph)
{
    graph.nodes[0] = *new Node(0);

    for (int i = 1; i < graph.nodes.size(); i++)
    {
        Edge newEdge(0, graph.nodes[0], graph.nodes[i]);//构造当前边
        graph.edges.push_back(newEdge);
        graph.nodes[i].in++;
        graph.nodes[0].out++;
    }
    return graph;

}

BellmanFord算法

//BellmanFord算法
bool BellmanFord(Graph graph, Node head, map& distanceMap)
{

    for (int i = 1; i < graph.nodes.size(); i++)
    {   
        Node node = graph.nodes[i];
        distanceMap[node] = MAX;
    }
    distanceMap[head] = 0;             //源节点到自身的距离设为0

    //n-1次循环求最短路径
    for (int i = 1; i < graph.nodes.size(); i++)
    {
        for (int j = 0; j < graph.edges.size(); j++)
        {
            if (distanceMap[graph.edges[j].to] > distanceMap[graph.edges[j].from] + graph.edges[j].weight)
            {
                distanceMap[graph.edges[j].to] = distanceMap[graph.edges[j].from] + graph.edges[j].weight;
            }
        }
    }   

    bool flag = true;  //标记是否有负权回路

    //第n次循环判断负权回路
    for (int i = 0; i < graph.edges.size(); i++)
    {
        if (distanceMap[graph.edges[i].to] > distanceMap[graph.edges[i].from] + graph.edges[i].weight)
        {
            flag = false;
            break;
        }
    }

    return flag;
}

对各边重新赋值,该部分代码和构建有向图类似

Graph ResetWeight(Graph graph, map& distanceMap, vector> matrix)
{
    for (int i = 0; i < graph.edges.size(); i++)
    {
        graph.edges[i].weight = graph.edges[i].weight + distanceMap[graph.edges[i].from] - distanceMap[graph.edges[i].to];
    }

    Graph Newgraph;
    int j = 0;

    for (int i = 0; i < matrix.size(); i++)
    {
        int from = matrix[i][0];
        int to = matrix[i][1];
        int weight = graph.edges[j++].weight;
        if (Newgraph.nodes.find(from) == Newgraph.nodes.end())
        {
            Newgraph.nodes[from] = *new Node(from);
        }
        if (Newgraph.nodes.find(to) == Newgraph.nodes.end())
        {
            Newgraph.nodes[to] = *new Node(to);
        }

        Newgraph.nodes[from].nexts.push_back(Newgraph.nodes[to]);//出发点指向的点中 加入 终点
        Newgraph.nodes[from].out++;//出发点出度++
        Newgraph.nodes[to].in++;//终点入度++

        Edge newEdge(weight, Newgraph.nodes[from], Newgraph.nodes[to]);//构造当前边
        Newgraph.edges.push_back(newEdge);
        Newgraph.nodes[from].edges.push_back(newEdge);//出发点的边集中加入当前边
    }
    return Newgraph;
}

Dijkstra算法

Node getMinDistanceAndUnselectedNode(map distanceMap, set touchedNodes)
{
    Node minNode =* new Node(-1);
    int minDistance = MAX;
    for (auto entry : distanceMap)
    {
        Node node = entry.first;
        int distance = entry.second;
        if (touchedNodes.find(node) == touchedNodes.end() && distance < minDistance)
        {
            minNode = node;
            minDistance = distance;
        }
    }
    return minNode;

}

//Dijkstra算法
map Dijkstra(Graph graph, Node head)
{
    //head 出发到所有点的最小距离
    //key:从head出发到到达key
    //value:从head出发到达key的最小距离
    //如果在表中,没有T的记录,则从head出发到T这个点的距离为正无穷
    map distanceMap;
    distanceMap[head] = 0;   //将头节点放入
    //已经求过距离的节点,存在selectedNodes中,以后再也不碰
    set selectedNodes;
    Node minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);    //从distanceMap中寻找除了selectedNodes以外的最短距离点
    while ( minNode.value != -1)
    {
        int distance = distanceMap[minNode];
        for (Edge edge : graph.nodes[minNode.value].edges)
        {
            Node toNode = edge.to;
           if(distanceMap.find(toNode) == distanceMap.end())
                distanceMap[toNode] = distance + edge.weight;
            distanceMap[edge.to] = min(distanceMap[toNode], distance + edge.weight);
        }
        selectedNodes.insert(minNode);
        minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);

    }

    return distanceMap;
}

Dijkstra的结果矩阵如下图所示:

Johnson算法_第1张图片

 第一行为1号节点到各点的最短距离,如第一行第一列的0表示1号节点到1号节点的最短距离为0,第一行第二列的2表示1号节点到2号节点的最短距离为2……以此类推。

Main(),再主函数中得到结果矩阵

int main()
{
    std::cout << "Hello World!\n";
    int cols = 3;
    cout << "请输入点数" << endl;
    int num = 0;
    cin >> num;
    vector> matrix = { {1,2,3},{1,5,-4},{1,3,8},{2,5,7},{2,4,1},{3,2,4},{4,1,2},{4,3,-5},{5,4,6} };
 
    /* 手动输入时的代码
    int rows = 0;
    cout << "请输入边数" << endl;
    cin >> rows;
    vector> matrix(rows, vector(cols, 0));
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
        {
            cin >> matrix[i][j];
        }
 */ 
    std::cout << "Process……\n";
    //创建图
    Graph graph = CreateGraph(matrix);

    //新增虚拟节点
    Graph NewGraph = CreateNewGraph(graph);

    //BellmanFord算法
    map BFdistanceMap;
    if (!BellmanFord(NewGraph, NewGraph.nodes[0], BFdistanceMap))    //BFdistanceMap里是δ(s,v),即h(v)
        std::cout << "the input graph contains a negative-weight cycle"< SinglePointDJ;
    vector> ResultDJ;
    for (int i = 1; i <= num; i++)
    {
        Node head = graph.nodes[i];
        map distanceMap = Dijkstra(graph, head);
        for (int j = 1; j <= num; j++)
        {
            SinglePointDJ.push_back(distanceMap[graph.nodes[j]]);
            cout << distanceMap[graph.nodes[j]] << "    ";
        } 
        ResultDJ.push_back(SinglePointDJ);
        cout << endl;
        SinglePointDJ.clear();

    }

    vector> ResultD = ResultDJ;

    for (int i = 0; i < ResultDJ.size(); i++)
    {
        for (int j = 0; j < ResultDJ[0].size(); j++)
        {
            ResultD[i][j] = ResultDJ[i][j] - BFdistanceMap[graph.nodes[i+1]] + BFdistanceMap[graph.nodes[j+1]];
            cout << ResultD[i][j]<<"     ";
        }
        cout << endl;

    }  


    std::cout << "finish!\n";

}

得到最终的结果矩阵。

Johnson算法_第2张图片

你可能感兴趣的:(算法,数据结构)