Dijkstra算法介绍以及C++实现

一、Dijkstra 算法简介

Dijkstra(迪杰斯特拉)算法是一种单源最短路径算法,用于在带非负权重的图中,计算某个起点到其它所有顶点的最短距离。它由荷兰计算机科学家 Edsger W. Dijkstra 在 1956 年提出。

基本思想

Dijkstra 算法的核心思路是贪心策略

每次从当前未访问的节点中选择距离起点最近的一个,确定它的最短路径,然后用它去更新其它节点的最短距离,直到所有节点都确定最短路径。

工作过程

假设:

  • 图有 VVV 个顶点
  • dist[] 数组记录起点到各顶点的最短距离
  • visited[] 记录哪些顶点已经确定了最短路径

步骤:

  1. 初始化:

    • dist[start] = 0
    • 其他 dist[v] = ∞
    • visited[] 全部为 false
  2. 从未访问的顶点中,选取 dist 最小的节点 u,标记 visited[u] = true

  3. 遍历 u 的所有邻居 v:

    • 如果 dist[v] > dist[u] + w(u,v),则更新 dist[v]
  4. 重复步骤 2~3,直到所有节点都被访问,或 dist 数组不再变化

伪代码

void dijkstra(int start, int n, vector<vector<pair<int,int>>>& graph) {
    vector<int> dist(n, INT_MAX);
    vector<bool> visited(n, false);
    dist[start] = 0;

    for (int i = 0; i < n; i++) {
        int u = -1, minDist = INT_MAX;
        for (int j = 0; j < n; j++) {
            if (!visited[j] && dist[j] < minDist) {
                u = j;
                minDist = dist[j];
            }
        }
        if (u == -1) break; // 剩下的不可达

        visited[u] = true;
        for (auto [v, w] : graph[u]) {
            if (!visited[v] && dist[v] > dist[u] + w) {
                dist[v] = dist[u] + w;
            }
        }
    }

    // 输出结果
    for (int i = 0; i < n; i++)
        cout << "Start -> " << i << " : " << dist[i] << endl;
}

如果用 优先队列(堆)优化,复杂度可以从 O(V2)O(V^2)O(V2) 降到 O((V+E)log⁡V)O((V+E)\log V)O((V+E)logV)

二、C++ 实现示例

下面的示例代码演示了如何在 C++ 中使用优先队列(priority_queue)来实现单源最短路径的 Dijkstra 算法,并在 main 函数中进行简单测试。我们采用邻接表存储图,顶点编号默认从 0 开始。

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

// 使用结构体存储图中每条边的信息 (邻接表的节点),
// 其中邻接节点是 v,边权重是 w
struct Edge {
    int v;
    int w;
    Edge(int _v, int _w) : v(_v), w(_w) {}
};

// 输入:
//   graph - 邻接表, graph[u] 表示从 u 出发的所有边
//   source - 源点
// 输出:
//   返回 dist 数组,表示从 source 到每个顶点的最短距离
vector<int> dijkstra(const vector<vector<Edge>>& graph, int source) {
    // 顶点数
    int V = graph.size();

    // 初始化距离数组,都设为 "无限大"
    vector<int> dist(V, numeric_limits<int>::max());
    dist[source] = 0; // 源点距离置为 0

    // 小根堆(最小优先队列),
    // 队列中每个元素存储的是 pair<当前距离, 当前顶点>
    // 这样可以根据最小距离进行弹出
    priority_queue< pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>> > pq;

    // 先把源点入队
    pq.push({0, source});

    // 当队列不为空时,反复取出距离最小的顶点进行松弛
    while(!pq.empty()) {
        // 取出队首: 距离最小的顶点
        auto [currDist, u] = pq.top();
        pq.pop();

        // 如果队首的距离比 dist[u] 更大,说明已被更新过,跳过
        if(currDist > dist[u]) {
            continue;
        }

        // 松弛操作
        for(auto &edge : graph[u]) {
            int v = edge.v;
            int w = edge.w;
            // 如果 u->v 可以更新 dist[v]
            if(dist[u] + w < dist[v]) {
                dist[v] = dist[u] + w;
                pq.push({dist[v], v});
            }
        }
    }

    return dist;
}

// 测试
int main() {
    // 假设我们有一个包含 5 个顶点(0~4) 的有向图:
    // graph[u] 是从顶点 u 出发的所有带权边
    // 例如:edge(0->1, 权重10) 表示从0到1的边权重为10
    vector<vector<Edge>> graph = {
        { {1,10}, {4,3} },     // 0 -> 1 (10), 0 -> 4 (3)
        { {2,2}, {4,4} },      // 1 -> 2 (2),  1 -> 4 (4)
        { {3,9} },             // 2 -> 3 (9)
        { {2,7} },             // 3 -> 2 (7)
        { {1,1}, {2,8} }       // 4 -> 1 (1),  4 -> 2 (8)
    };

    int source = 0;  // 以0号顶点作为源点
    vector<int> dist = dijkstra(graph, source);

    // 输出结果
    cout << "从源点 " << source << " 到各顶点的最短距离:" << endl;
    for(int i = 0; i < (int)dist.size(); i++) {
        if(dist[i] == numeric_limits<int>::max()) {
            cout << "顶点 " << i << ": 无法到达" << endl;
        } else {
            cout << "顶点 " << i << ": " << dist[i] << endl;
        }
    }

    return 0;
}

运行示例
在这里插入图片描述

代码说明

  1. 邻接表的存储方式
    vector<vector<Edge>> graph;
    其中 graph[u] 存放从顶点 u 出发的所有边(Edge),Edge 结构体包含目标顶点 v 和边权重 w

  2. Dijkstra 函数

    • 使用 dist[v] 数组来存储从源点到顶点 v 的最短距离,初始都设为“无限大”,源点距离设为 0。
    • 使用一个小根堆(最小优先队列)来获取当前距离最小的未确定顶点。
    • 每弹出一个顶点 u,就尝试松弛 u 对应的邻接边,并更新相邻顶点的距离。
    • 松弛(relaxation)
      if (dist[u] + w < dist[v]) {
          dist[v] = dist[u] + w;
          // 将更新后的距离重新压入优先队列
          pq.push({dist[v], v});
      }
      
  3. main 函数中测试

    • 我们构造了一个简单的5个顶点的有向图,并在源点为 0 的情况下运行算法。
    • 将计算得到的最短路径距离输出到终端。

三、应用场景

地图导航
GPS、百度地图、高德地图等会用 Dijkstra 算法(或其优化版 A*)计算最短路径

网络路由
OSPF(开放式最短路径优先协议)就是基于 Dijkstra 算法构建路由表

游戏开发
NPC 移动寻路、AI 决策(与 A* 算法结合)

物流配送优化
计算送货路径、运输网络最短时间

通信网络优化
选择最小延迟的传输路径

机器人路径规划
移动机器人、AGV(自动导引车)寻路

四、注意事项

权重必须非负:如果存在负权边,应使用 Bellman-Ford 或 SPFA
适合稠密图:稀疏图建议用优先队列优化
单源最短路径:如果需要多源,通常要多次运行或使用 Floyd-Warshall

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

令狐掌门

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值