数据结构——Dijkstra算法(寻找图的最短路径)

Dijkstra算法

Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。注意该算法要求图中不存在负权边。

问题描述:在无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点 V0 到其余各点的最短路径。(单源最短路径)

算法概要

设置一个图G=(V,E),一个带权无向图。把图中的顶点分为两组,一组为集合S,另一组自然是集合V-S。第一组为已经求得的最短路径的点。(一开始,集合S当中只存在一个源点v0,在不断的求最短路径的途中,集合S不断加入新的点,当集合S=V时,算法结束)。第二组为还未求得的最短路径的点(记为V-S)。

按照最短长度递增的次序不断把集合V-S加入到S集合当中去。(注意:在加入的时候,要保证源点v0到集合S的点的距离要小于或等于v0到集合V-S上的点)。此外,每一个顶点有一个对应的距离,这个距离则是源点v0到该点的最短距离。S中的距离则是v0到该点的最短距离,V-S中的距离则是v0到该点,经过S中的某点作为中转的距离。

代码实现

这是我实现这个方法使用的类

class graph {

private:

    int graphSize;  //记录图的大小
    int **map;      //记录图
    int *path;      //记录最短路径
    int *weight;    //记录最短路径的长度

    //初始化图
    void initializeGraph(int v);
    //展示图的最短路径
    void displayShortestPath(int source);

public:

    //初始化一个size*size大的图
    graph(int size);

    //展示图
    void displayGraph();
    //寻找最短路径
    void findShortestPath(int sourcePoint);

};

这是我定义的一些宏

#define MAX_WEIGHT 100 //设置最大权值
#define CANNOT_CONNECTION -1 //设置路不连通所对应的值

以下是dijkstra算法的代码实现

//Dijkstra算法
void graph::findShortestPath(int sourcePoint)
{
    //若输入的源点不合法,则退出算法
    if (sourcePoint >= graphSize || sourcePoint < 0) {
        cout << "输入不合法" << endl;
        return;
    }


    /*
    算法概要

    建立顶点集,将顶点分为s,v-s两个顶点集
    s顶点集的顶点当中,设为true,v顶点集设为false(属于s集的就是true,不属于的就是false)
    从s顶点集开始,不断的向v顶点集扩展,而一开始s集合当中只存在源点sourcePoint

    a 计算源点到点i的距离,并记录(i=1,2,3。。。)

    b 将与源点距离最短的点u加入到s集合当中

    c 计算经过点u到点i的距离(i=1,2,3。。。。)

    d 若经过点u到点i的距离小于之前所计算出的距离,重置距离,并将点u设置为前往点i的父节点(i=1,2,3。。。。)

    重复b,c,d过程直到集合s包含了所有的点

    在计算结束之后,weight[i](我用于记录源点到点i最短距离的数组)里面的数据则是源点sourcePoint到点i的最短距离
    而path[i](我用于记录源点到点i的路径的数组)里面的数据则是点i到源点的路径的父节点
    想要找到完整的路径只需要不断的从path[i]中寻找父节点,直到找到一个父节点为源点sourcePoint的时候既可以找到完整的路径

    */
    bool *s_set = new bool[graphSize];//集合s

    /*
    算法初始化部分

    先将从源点sourcePoint到达其他所有点的距离记录下来
    (若该点与源点并无通道,则距离记录为无限大)

    将集合s的内容全部置为false,意为还没有开始运算

    将从源点sourcePoint到各点的路径记录下来
    因为一开始都是从源点到点i,因此路径的父节点全部都是源点sourcePoint
    (若点i与源点不相通,则记录为CANNOT_CONNECTION)
    */
    for (int i = 0; i < graphSize; i++) {

        //记录路程
        weight[i] = map[sourcePoint][i];
        //重置s集合
        s_set[i] = false;


        //记录路径
        if (weight[i] == MAX_WEIGHT)
            path[i] = CANNOT_CONNECTION;
        else
            path[i] = sourcePoint;
    }

    //从源点到源点的距离为0
    weight[sourcePoint] = 0;

    //这个算法是从源点开始计算的
    //所以源点一开始就在集合s当中
    s_set[sourcePoint] = true;

    //计算部分
    for (int i = 0; i < graphSize; i++) {

        int minWeight = MAX_WEIGHT;//记录最短路(一开始先置为最大)
        int u = sourcePoint;//源点为最开始的点

        //寻找从源点sourcePoint到不属于s集合的点u的最短距离的点
        for (int j = 0; j < graphSize; j++) {

            if (!s_set[j] && weight[j] < minWeight) {

                //记录这个节点
                u = j;
                //记录这个最短路程
                minWeight = weight[j];
            }

        }

        //将节点u加入到s集合当中
        s_set[u] = true;

        for (int j = 0; j < graphSize; j++) {

            //若点j不属于集合s,并且点u到点j之间存在道路
            if (!s_set[j] && map[u][j] != MAX_WEIGHT) {

                /*
                若通过点u前往点j的路程比直接前往点j的路程要短
                (也有可能是通过点u前往到点j的路程比之前计算出的前往点j的路程要短)
                则记录这个通向点j的路程
                并且将点u记录为前往点j的父节点
                */
                if (weight[u] + map[u][j] < weight[j]) {

                    //记录最短路
                    weight[j] = weight[u] + map[u][j];
                    //前往点u的父节点
                    path[j] = u;

                }

            }


        }

    }

    //展示路径
    displayShortestPath(sourcePoint);

}

展示路径

一些则是展示路径的方法

or (int i = 0; i < graphSize; i++) {

        //输出最短距离
        cout << "从第" << sourcePoint + 1 << "个点" << "到第" << i + 1 << "个点的最短距离为" << weight[i] << endl;

        //输出路径
        cout << "路径为:" << i+1 << "<-";
        int j = i;

        //若路径不是直达的路径
        if (path[i] != sourcePoint) {

            //不断寻找父节点,直到找到父节点为源点的时候
            while (sourcePoint!=j)
            {
                cout << path[j] + 1;
                if (sourcePoint != path[j]) {
                    cout << "<-";
                }

                j = path[j];
            }//end while

        }//end if
        else {

            //若为直达路径则输出i<-sourcePoint
            cout << sourcePoint+1;//因为数组是从0开始的而我的程序的节点的输入是从一开始的,所以输出的时候要补上+1;

        }//end else

        cout << endl;

    }//end for

运行结果

数据结构——Dijkstra算法(寻找图的最短路径)_第1张图片

你可能感兴趣的:(我的数据结构自学笔记)