Dijkstra(迪杰斯特拉)算法: 求单源最短路径(C++实现)

迪杰斯特拉算法: 求单源最短路径,即给定图G(V,E)和起点S,求起点S到达其他各个点的最短路径。

算法策略

(1)每次从集合中(图的各个节点) 选择与起点S距离最小且未访问的节点,记为u,访问u,并将其加入已访问的集合。

(2)将结点u作为中介点,访问其能到达的其他结点。如果以u作为中介点,从起点S到达其他结点的距离小于当前记录的起点S到达其他结点的距离,则进行路径长度的优化。(初始化起点S到达其他结点的距离为无穷大,代码中设为一个很大的数)

(3)回到步骤1,继续寻找下一个未访问的且距离最小的节点。

算法的策略类似一种贪心的思想,每次都访问最短的路径,而且如果以这个最短的路径为中介,可以优化起点到其他节点的距离,则将以此路径作为中介,更新起点到其他点的距离。

接下来是代码实现,分为两种类型,根据图的不同实现来区分。一种是邻接表类型,另一种是邻接矩阵类型。

1、使用邻接矩阵实现图,进行Dijkstra算法的实现

#include <vector>
#include <iostream>
using namespace std;

const int MAXV = 1000;        // 初始化节点数量
const int INF = 1000000000;   // 初始化距离为很大的一个数


int n, G[MAXV][MAXV]; // 图的邻接矩阵实现
int d[MAXV];          // 存储起点到各个点的最短路径长度
bool vis[MAXV] = {
    
     false }; // 标记结点是否被访问

void Dijkstra(int s) {
    
    
    fill(d, d + MAXV, INF);  // 初始化距离为 “无穷大”
    d[s] = 0; // 起点到自身的距离为0
    for (int i = 0; i < n; ++i) {
    
    
        // 找到路径最短的且还未访问的节点
        int u = -1, mid_dis = INF;
        for (int j = 0; j < n; ++j) {
    
    
            if (vis[j] == false && d[j] < mid_dis) {
    
    
                u = j;
                mid_dis = d[j];
            }
        }

        // 如果找不到节点访问,则说明剩下的节点和当前起点不通
        if (u == -1) return;

        // 标记节点被访问过
        vis[u] = true; 

        // 遍历所有的节点, 优化路径长度
        for (int v = 0; v < n; ++v) {
    
    
            // 如果 v 未被访问 且 u能够到达v 且 以u为中介点可以使d[v]更优
            if (vis[v] == false && G[u][v] != INF && d[u] + G[u][v] < d[v]) {
    
    
                d[v] = d[u] + G[u][v];
            }
        }
    }
}

2、使用邻接表实现图,进行Dijkstra算法的实现

#include <vector>
#include <iostream>
using namespace std;

// 图的节点结构体
struct node {
    
    
    int v;   // 边的目标顶点
    int dis; // 两个顶点之间的边权值
};

int n;        // 结点个数
int d[MAXV];  // 存储起点到其他节点的最短路径
vector<node> adj[MAXV]; // 图的邻接表实现
bool vis[MAXV] = {
    
     false }; // 存储已访问过的结点

void Dijkstra(int s) {
    
    
    fill(d, d + MAXV, INF);
    d[s] = 0; // 起点
    for (int i = 0; i < n; ++i) {
    
    
        // 找到未访问结点中距离最小的结点
        int u = -1, min_dis = INF;
        for (int j = 0; j < n; ++j) {
    
    
            if (vis[j] == false && d[j] < min_dis) {
    
    
                u = j;
                min_dis = d[j];
            }
        }

        // 如果没有找到,说明剩余结点和当前起点不连通
        if (u == -1) return;

        // 找到后,标记结点为已访问
        vis[u] = true;

        // 遍历当前结点能到达的其他结点
        for (int j = 0; j < adj[u].size(); ++j) {
    
    
            int v = adj[u][j].v;
            if (vis[v] == false && d[u] + adj[u][j].dis < d[v]) {
    
    
                d[v] = d[u] + adj[u][j].dis; // 最短距离优化
            }
        }
    }
}

如果是无向边,则在图的实现的时候,进行双向填充即可:

// 邻接矩阵
G[u][v] = G[v][u] = x; // x = 权值

// 邻接表
adj[u].emplace_back(v);
adj[v].emplace_back(u);

注意:如果图中边权值出现负值,Dijkstra算法就会失效,因为这样会无法保证边的权值随着访问始终在增大,可能访问了很多节点后,权值却为0。这个时候可以使用Bellman-Ford算法和SPFE算法。

如有不足,请指出。

谢谢阅读。

参考《算法笔记》

猜你喜欢

转载自blog.csdn.net/weixin_43869898/article/details/110263080