最短路径-Dijkstra算法

一、Dijkstra算法

迪杰斯特拉算法(Dijkstra)用来计算从一个点到其他所有点的最短路径的算法,是一种单源最短路径算法。也就是说,只能计算起点只有一个的情况。它不能处理存在负边权的情况。

利用 dist 数组记录当前各结点与起点的已找到的最短路径长度;
每次从未扩展的结点中选取 dist 值最小的结点 v 进行扩展,更新与 v 相邻的结点的 dist 值;
不断进行上述操作直至所有结点均被扩展,此时 dist 数据中记录的值即为各结点与起点的最短路径长度。


代码实现一:时间复杂度是O(V2)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**************************************************************** 
 * Description: C++ implementation  
 * How to find Shortest Paths from Source to all Vertices using Dijkstra’s Algorithm
 * Author: Alex Li
 * Date: 2023-07-05 21:00:29
 * LastEditTime: 2023-07-20 21:17:38
****************************************************************/

#include <iostream>
using namespace std;
const int MAXV = 100;
int n, i , j, v;
int w[MAXV][MAXV]; // 邻接矩阵,记录边长
int pre[MAXV]; //每个结点的前驱
int dist[MAXV];  //存储0点到其它结点的最短路径长度
int used[MAXV]; // 记录结点是否已确定最短距离(0:未确定;1:已确定)

//输出结点最短路径
void Path(int u){
    if(u==-1)return;
    Path(pre[u]);
    cout<<u<<" ";
}
int main() {
    cin >> n;  //n为结点数
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            cin >> w[i][j]; //录入边长
    dist[0] = 0;  //0点到自身的距离为0
     //初始化所有结点距离
    for (i = 1; i < n; i++)dist[i] = -1; 
    //初始化所有结点的前驱
    for (int i = 0; i <n; i++)pre[i]=-1;
    //初始化所有结点为未扩展状态
    for (i = 0; i < n; i++)used[i] = 0;
    
    while (true) {
        v=-1 ;

//v刚开始赋的是-1,意思是还没找到距离最近的点,在循环里满足判断条件的i结点,也就是dist最短的结点
//会赋给v(因为v==-1是在||左边的,当||左边为真时右边不参与运算,不会越界)
//当所有结点都已经used[i]=1,则v=-1,while循环结束。        
        for (i = 0; i < n; i++)
            if (used[i] != 1 && dist[i] != -1 && (v == -1 || dist[i]<dist[v] ))
            v=i;

//如果没有找到哪个结点还没被访问,或者没有距离的,说明所有结果都已经找好最短路径,v==-1,结束循环。
        if(v == -1)
            break;
        used[v]=1;

//寻找所有v结点的相邻结点,如果没有距离,或者从v点连接距离较短,更新路径和路径前驱
        for (i = 0; i < n; i++)
            if (w[v][i] != -1 && (dist[i] == -1 || dist[v]+w[v][i]<dist[i] )){
                dist[i] = dist[v] + w[v][i];
                pre[i]=v;
            }
                
        //依次更新每个点所到相邻的点路径值
    }

//打印每个结点的最短路径的值和经过的结点
    for (i = 0; i < n; i++){
        cout << "node["<<i<<"]distance is "<<dist[i] <<" and the path is ";
         Path(i);
        cout<< endl;
    }
    return 0;
}

测试数据:

9
-1 4 -1 -1 -1 -1 -1 8 -1
-1 -1 8 -1 -1 -1 -1 11 -1
-1 8 -1 7 -1 4 -1 -1 2
-1 -1 7 -1 9 14 -1 -1 -1 
-1 -1 -1 9 -1 10 -1 -1 -1
-1 -1 4 14 10 -1 2 -1 -1
-1 -1 -1 -1 -1 2 -1 1 6
8 11 -1 -1 -1 -1 1 -1 7
-1 -1 2 -1 -1 -1 6 7 -1 

代码实现二: 采用优先队列和邻接表, 时间复杂度是O(ELogV))

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/**************************************************************** 
 * Description: dijkstra-priority_queue
 * Author: Alex Li
 * Date: 2024-07-10 18:05:31
 * LastEditTime: 2024-07-10 19:06:05
****************************************************************/

#include <iostream>
#include <vector>
#include <queue>
#include <climits>

using namespace std;
//dijkstra 算法,使用优先队列
vector <int> dijkstra(int V, vector <vector<pair<int, int> > > &adj, int src){

//创建一个优先队列,存储被处理的结点。 pair.first是权重,pair.second是结点,排序是小的在队列前面,pair(distance, vertex)
priority_queue<pair<int, int>,vector<pair<int, int>>,greater<pair<int,int>>> pq; 
//存结点到起始点的距离,初始值为INT_MAX
vector<int> dist(V, INT_MAX);
//把起始结点放到优先队列里,
pq.push({0, src});
//起始结点到自己的距离是0
dist[src] = 0;

//循环,知道优先队列为空
while (!pq.empty()) {
        // 优先队列中最上面的结点是到起始点距离最短的,拿出来给到u,然后弹出
        int u = pq.top().second;
        pq.pop();

        // 遍历所有和u相邻的结点,
        for (auto& neighbor : adj[u]) {
            int v = neighbor.first;
            int weight = neighbor.second;

            // 如果相邻的结点通过u结点到起始点更短,哪就更新他们到起始点的距离。并把它放到优先队列中。
            if (dist[u] + weight < dist[v]) {
                dist[v] = dist[u] + weight;
                pq.push({dist[v], v});
            }
        }
    }
   //返回距离数组
    return dist;

}

int main(){
    int V=5;
    //cin>>V;  //V是图中的结点数
    vector<vector<pair<int,int> > > adj(V); //利用邻接表存图 pair.first存结点,pair.second存权重
    
    //添加图的数据,addEdge(u,v,w) 加一条从u到v的边,权重是w
    adj[0].push_back({1, 10});
    adj[0].push_back({4, 3});
    adj[1].push_back({2, 2});
    adj[1].push_back({4, 4});
    adj[2].push_back({3, 9});
    adj[3].push_back({2, 7});
    adj[4].push_back({1, 1});
    adj[4].push_back({2, 8});
    adj[4].push_back({3, 2});

    int src=0; //起始结点
    
    vector <int> distance=dijkstra(V,adj,src);

    cout<<"Vertex\tDistance from Source"<<endl;
    for(int i=0;i<V;i++) cout<<i<<'\t'<<distance[i]<<endl;
    
}


习题:单源最短路径[P3371]

题目描述

如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入格式

第一行包含三个整数 n,m,s,分别表示点的个数、有向边的个数、出发点的编号。
接下来 m行每行包含三个整数 u,v,w,表示一条 u→v的,长度为 w的边。

输出格式

输出一行 n 个整数,第 i个表示 s 到第 i个点的最短路径,若不能到达则输出 231−1。

输入输出样例

输入 #1

4 6 1 
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4

输出 #1

0 2 4 3

P4779

Scroll to Top