有向图中单个源点到终点的最短路径--Dijkstra算法与实现

1、Dijkstra算法能够解决有向图中单个源点到另一终点的最短路径问题,它的算法过程如下:

1)用矩阵graph[]N[N](N为图中节点个数)表示带权的有向图G。若图中两个节点vi和vj是连通的,则graph[i][j]表示这两个节点之间边的权值;若两节点vi和vj不是连通的,则graph[i][j] = -1.

2)设S为从某点start_vec开始的最短路径path的终点集合,初始状态时,集合S中只有起始节点start_vec.设从起始节点start_vec到其余节点vi的最短路径长度为D[i]. 设剩余节点集合remanderLis,它表示未被选中的剩余节点,初始状态remanderLis 包含除起始节点start_vec之外图中任一节点。

3)对于非起始节点vi,初始化D[i] = graph[start_vec][i].

4)从remanderLis中选择一个节点,该节点是由起始节点经过当前已选中节点能够到达的节点中路径最短的节点,方法是比较当前的D数组D[j],从中选出最小的值D[j]且满足S中没有节点vj. 设选中的节点为min_vec, 将min_vec加入集合S中,并从集合remanderLis中删除节点min_vec.

5)修改从起始节点start_vec到任一剩余节点vk(集合remanderLis)的最短路径长度D[k]。若节点vk与min_vec之间有边,则视情况更新D[k],若D[k] == -1,即start_vec到vk没有边,D[k] = D[min_vec] + graph[min_vec][k];若D[k] != -1,则判段  D[k] = min{D[k], D[min_vec] + graph[min_vec][k]};

6)重复4到5过程,直到remanderLis集合为空。

2、下面给出图片说明(参考别人的)

《有向图中单个源点到终点的最短路径--Dijkstra算法与实现》

《有向图中单个源点到终点的最短路径--Dijkstra算法与实现》
                                 
《有向图中单个源点到终点的最短路径--Dijkstra算法与实现》


从运算过程表中,我们可知v0到其余个点的最短路径,如下图

《有向图中单个源点到终点的最短路径--Dijkstra算法与实现》

3、下面给出代码实现,我们知道Dijkstra算法肯定要输出源点到终点的路径,下面的算法中将会实现,简单说明就是在每次更新D[K]的时候更新数组pathArr,pathArr[k]用于存储由起始节点到节点vk最短路径上vk的上一个节点,当从起始节点到终点的路径过程中,每一个非起始节点都会存储它的上一个节点,所以只要从终点节点开始,遍历pathArr数组就可以找到从起始节点到终点的路径。本算法实现还有一个特殊的地方是采用-1来表示两个节点之间无边,你可能会好奇这样设置,到时找最小边的时候怎么办,任何有边的两点之间的权值都会大于-1,这能行吗?答案肯定的,下面的算法实现中会展现出来。好了,no bb, show you code.

#include <iostream>
#include <list>
#include <vector>
#include <stack>
#include <climits>
using namespace std;

#define N 6

int graph[N][N] = /*{-1, -1, 10, -1, 30, 100,
                    -1, -1, 5, -1, -1, -1,
                    -1, -1, -1, 50, -1, -1,
                    -1, -1, -1, -1, -1, 10,
                    -1, -1, -1, 20, -1, 60,
                    -1, -1, -1, -1, -1, -1 };*/
                    { -1, 10, 12, -1, -1, -1,
                      -1, -1, -1, 16, 25, -1,
                      4, 3, -1, 12, -1, 8,
                      -1, -1, -1, -1, 7, -1,
                      -1, -1, -1, -1, -1, -1,
                      -1, -1, -1, 2, 10, -1
                    };

void dijkstra_algorithm(int graphArcs[][N], int start_vec, int end_vec)
{
    int D[N] = {0};              //从起始点到其他点的最短路径,初始化为0
    int selectedVecs[N] = {0};
    selectedVecs[start_vec] = 1;            //开始只有起始节点被选中,其他节点未被选中
    vector<int> pathVecs;        //在起始点到终点的最短路径过程中,vector[i]存储节点i的上一个节点
    list<int> remanderLis;       //剩余的节点集合
    int i;
    for(i = 0; i < N; i++)
    {
        if(i == start_vec)
            continue;
        remanderLis.push_back(i);
        D[i] = graphArcs[start_vec][i];           //初始情况下初始节点到其他节点的最短路径长度
        //cout << D[i] << " ";
        pathVecs.push_back(start_vec);              //初始时候,将每个节点的上一个节点初始化为起始节点
    }
    //cout << endl;
    while(!remanderLis.empty())         //没有剩余节点后或找到终点路径,则退出循环
    {
        int j, minVec;
        unsigned int minArc = UINT_MAX;  //这里定义无符号整形的作用是方便和-1比较,无符号整数 < -1
        for(j = 0; j < N; j++)           //从未被选中的剩余节点中,找出距离起始节点最近的节点
        {
            if(selectedVecs[j] != 0)
                continue;
            if(minArc > D[j])               //当D[j]=-1时,表示节点j与起始点之间没有路径,且无符号整形与-1比较时,-1更大
            {
                minArc = D[j];
                minVec = j;
            }
        }
        if(selectedVecs[minVec] == 0)
        {
            selectedVecs[minVec] = 1;         //在未被选中的点中距离起始节点最近的点将被选中
            remanderLis.remove(minVec);     //删除已经被选中的节点
        }
        if(selectedVecs[end_vec] == 1)      //若选中了end_vec,则退出循环
            break;
        //选人新节点后,更新D[]数组
        for(j = 0; j < N; j++)
        {
            if(selectedVecs[j] != 0)
                continue;                           //已被选中节点则跳过
            if(graphArcs[minVec][j] != -1)          //选中节点和各个节点直接有路径
            {
                if(D[j] == -1)            //起始节点到节点j没有路径
                    D[j] = D[minVec] + graphArcs[minVec][j];
                else if(D[j] > D[minVec] + graphArcs[minVec][j])
                    D[j] = D[minVec] + graphArcs[minVec][j];
                pathVecs[j] = minVec;
            }
        }
    }
    if(D[end_vec] == -1)
    {
        cout << "sorry, can't find the path from " << start_vec << " to " << end_vec << endl;
        return;
    }
    cout << "the path length from " << start_vec << " to " << end_vec << " is: " << D[end_vec] << endl;
    cout << "the path form " << start_vec << " to " << end_vec << " is: ";
    //vector<int>::iterator iter = pathVecs.rbegin();
    stack<int> pathS;
    pathS.push(end_vec);
    i = end_vec;
    do
    {
        pathS.push(pathVecs[i]);
        i = pathVecs[i];
    }while(i != start_vec);

    while(!pathS.empty())
    {
        cout << pathS.top() << " ";
        pathS.pop();
    }
}

int main()
{
    dijkstra_algorithm(graph, 0, 4);

    return 0;
}

运行结果如下:

《有向图中单个源点到终点的最短路径--Dijkstra算法与实现》

是不是挺简单的。

    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/vgxpm/article/details/48056489
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞