Dijkstra算法

一、Dijkstra算法简介

Dijkstra算法是解决单源最短路径问题的贪心算法

该算法在计算的时候将所有的点分为两个集合,一个是目标点集U,初始时只有起点,Dijkstra算法的功能是,给定一个起点,计算它到其他所有点的最短路径

二、Dijkstra算法的基本思想

  1. 设置两点顶点的集合U和T,集合U中存放已找到最短路径的顶点,集合T 中存放当前还未找到的最短路径的顶点.
  2. 初始状态时,集合U中只包含源点,设为v0。
  3. 然后从集合T 中选择到源点v0路径长度最短的顶点u加入到集合U中
  4. 集合U中每加入一个新的顶点u都要修改源点带集合T 中剩余项点的当前最短路径值,集合T 中各项点的新的当前最短路径长度值,为 原来的当前最短路径长度值从源点过顶点u到达该顶点的带权路径长度 中的较小者
  5. 回到3,此过程不断重复,直到集合T中的顶点全部加入到集合U中为止。

三、图片例子

(一)例子1

看那个路线少加入那个,注意分辨虚线和实线

 

 

 

上述例子中,有图中圆圈中的字母代表节点的信息,圆圈上面的内容代表源点到其最短距离以及前驱节点,前驱节点用于后续重现最短路径。

这里主要是使用到了一个简单的定理,如果起点v0到目标点u之间的某一条路径是最短路径,那么在该路径上面,任何一个点u到的路径都是u'到v的最短路径。这个证明十分简单,使用反证法即可。

那么这里在记录最短路径的信息时,如果需要重现路径中的顶点,那么只需要对于每一个结点设置一个前驱结点即可。

(二)例子2

这里使用表格的形式来对整个算法流程进行分析。

四、实现

(一)代码

#include<cstdio>
using namespace std;
#define MAX 10000
#define ING 20000
int n, e;                           //顶点数n,边的条数e
int cost[20][20];                   //临界矩阵
int dist[20];                      //存储最短路径的长度值
int pre[20];                       //存储一个顶点在其最短路径的前趋
int S[20];                         //标志数组,若为已经找到最短路径的结点则为1,否则为0

void Dijkstra(int v) {
    for (int i = 0; i < n; i++) {
        dist[i] = cost[v][i];        //初始化
        S[i] = 0;                    //标志位初始为0
        if (dist[i] < MAX) {
            pre[i] = v;              //若存在边,则前趋为原点
        }
        else {
            pre[i] = -1;             //否则,前趋为-1,不可达
        }
    }
    S[0] = 1;                        //原点标志为1    
    for (int i = 0; i < n - 1; i++) {        //循环n-1次
        int u;                     //u为待选顶点
        int min = ING;         //令初始最小值>max,使距离值为max的顶点也能加到S中
        for (int j = 0; j < n; j++) {
            if ((!S[j]) && dist[j] < min) { //寻找距离S最小的顶点u
                min = dist[j];
                u = j;
            }
        }
        S[u] = 1;                    //将其标志设置为1
        for (int k = 0; k < n; k++) {      //调整未加入S的点的的距离值
            if ((!S[k]) && dist[k] > dist[u] + cost[u][k]) {
                dist[k] = dist[u] + cost[u][k];
                pre[k] = u;          //若通过u减小了k的距离,则修改k的前趋为u
            }
        }
    }
    printf("\nThe result:\n");     //输出结果
    for (int i = 0; i < n; i++) {
        printf("<%d,%d>:  ", v + 1, i + 1);
        int p = pre[i];
        if (p != -1) {                 //若可达输出最短路径

            printf("%d  ", dist[i]);    //输出最短距离

            printf("%d", i + 1);      //根据前趋逆向输出最短路径
            while (p != v) {
                printf("<--%d", p + 1);
                p = pre[p];
            }
            printf("<--%d", v + 1);
        }
        else {                      //若不可达则输出“inf”
            printf("inf");
        }
        printf("\n");
    }
}

int main() {
    printf("Please enter the number of n and e:");
    scanf_s("%d%d", &n, &e);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cost[i][j] = MAX;        //初始化为max
        }
    }
    int start, end, dut;
    for (int i = 0; i < e; i++) {
        printf("Please enter the number of start, end, dut:");
        scanf_s("%d%d%d", &start, &end, &dut);      //输入边的始点,终点和权值
        start--;
        end--;                     //结点号与存储的下标相差1
        cost[start][end] = dut;
    }
    int v = 0;
    Dijkstra(v);                   //以顶点1(即下标为0)为原点v
    return 0;
}

(二)时间复杂度

V-节点个数,E-边个数

Dijkstra算法的总运行时间依赖于最小优先队列的实现。

1、数组 —— O( V+ E ) —— O( V)

2、二叉堆 —— O( (V+E)logV )

3、斐波那契堆 —— O( VlogV + E )

五、补充

该算法只能计算边的权重值大于0的情况,对于边权值小于0的一些情况,该算法无法进行正确计算

能正确进行计算:

 不能正确计算:

posted @ 2023-02-13 22:06  ImreW  阅读(131)  评论(0编辑  收藏  举报